Co to jest wyjątek NullPointerException i jak go naprawić?


210

Co to są wyjątki zerowego wskaźnika ( java.lang.NullPointerException) i co je powoduje?

Jakich metod / narzędzi można użyć do ustalenia przyczyny, aby zatrzymać wyjątek powodujący przedwczesne zakończenie działania programu?

Odpowiedzi:


3764

Kiedy deklarujesz zmienną odniesienia (tj. Obiekt), tak naprawdę tworzysz wskaźnik do obiektu. Rozważ następujący kod, w którym deklarujesz zmienną typu pierwotnego int:

int x;
x = 10;

W tym przykładzie zmienną xjest an, inta Java zainicjuje ją 0dla Ciebie. Kiedy przypisujesz mu wartość 10w drugim wierszu, twoja wartość 10jest zapisywana w lokalizacji pamięci, o której mowa x.

Ale kiedy próbujesz zadeklarować typ odwołania , dzieje się coś innego. Weź następujący kod:

Integer num;
num = new Integer(10);

Pierwszy wiersz deklaruje zmienną o nazwie num, ale tak naprawdę nie zawiera pierwotnej wartości. Zamiast tego zawiera wskaźnik (ponieważ typ jest Integertypem odniesienia). Ponieważ nie powiedziałeś jeszcze, na co ma wskazywać, Java ustawia to null, co oznacza „ Nie wskazuję na nic ”.

W drugim wierszu newsłowo kluczowe służy do tworzenia (lub tworzenia) obiektu typu, Integera zmienna wskaźnikowa numjest przypisana do tego Integerobiektu.

NullPointerExceptionWystępuje podczas zadeklarować zmienną, ale nie utworzyć obiekt i przypisać do zmiennej przed próbują wykorzystać zawartość zmiennej (tzw wyłuskania ). Wskazujesz więc na coś, co tak naprawdę nie istnieje.

Dereferencje zwykle występują podczas .uzyskiwania dostępu do metody lub pola lub [indeksowania tablicy.

Jeśli spróbujesz wyrejestrować numPRZED utworzeniem obiektu, otrzymasz NullPointerException. W najbardziej trywialnych przypadkach kompilator złapie problem i poinformuje, że „ num may not have been initialized,”, ale czasami możesz napisać kod, który nie tworzy bezpośrednio obiektu.

Na przykład możesz mieć następującą metodę:

public void doSomething(SomeObject obj) {
   //do something to obj
}

W takim przypadku obiekt nie jest tworzony obj, a raczej zakładany, że został utworzony przed doSomething()wywołaniem metody. Uwaga: możliwe jest wywołanie metody w ten sposób:

doSomething(null);

W takim przypadku objjest null. Jeśli metoda ma na celu zrobienie czegoś z przekazanym obiektem, należy zgłosić błąd, NullPointerExceptionponieważ jest to błąd programisty, a programista będzie potrzebował tych informacji do celów debugowania. Podaj nazwę zmiennej obiektowej w komunikacie wyjątku, np

Objects.requireNonNull(a, "a");

Alternatywnie mogą wystąpić przypadki, w których celem metody nie jest wyłącznie działanie na przekazanym obiekcie, a zatem parametr zerowy może być akceptowalny. W takim przypadku należy sprawdzić parametr zerowy i zachowywać się inaczej. Powinieneś także wyjaśnić to w dokumentacji. Na przykład doSomething()można zapisać jako:

/**
  * @param obj An optional foo for ____. May be null, in which case 
  *  the result will be ____.
  */
public void doSomething(SomeObject obj) {
    if(obj == null) {
       //do something
    } else {
       //do something else
    }
}

Wreszcie, jak wskazać wyjątek i przyczynę za pomocą śledzenia stosu

Jakich metod / narzędzi można użyć do ustalenia przyczyny, aby zatrzymać wyjątek powodujący przedwczesne zakończenie działania programu?

Sonar z findbugs może wykryć NPE. Czy sonar może wychwytywać wyjątki wskaźnika zerowego spowodowane przez JVM Dynamicznie


558
„Najlepszym sposobem uniknięcia tego rodzaju wyjątku jest zawsze sprawdzanie, czy nie ma wartości pustej, gdy sam nie utworzyłeś obiektu.” Jeśli obiekt wywołujący przyjmuje wartość null, ale null nie jest poprawnym argumentem dla metody, poprawne jest zwrócenie wyjątku z powrotem do obiektu wywołującego, ponieważ jest to wina osoby wywołującej. Ciche ignorowanie niepoprawnych danych wejściowych i niepodejmowanie żadnych działań w metodzie jest bardzo kiepską radą, ponieważ ukrywa problem.
Boann

104
Dodałbym uwagę na temat tego postu wyjaśniającą, że nawet przypisania do prymitywów mogą powodować NPE podczas korzystania z autoboxowania: int a=bmogą rzucać NPE, jeśli b jest an Integer. Są przypadki, w których debugowanie jest mylące.
Simon Fischer,

58
Czy możliwe jest przechwycenie NPE wyrzuconego przez aplikację internetową z przeglądarki internetowej? Czy będzie to widoczne w źródle strony widoku z przeglądarki internetowej?
Sid

76
Tak, sprawdź, czy obiekt jest równy null przed wywołaniem na nim metody lub spróbuj uzyskać dostęp do zmiennej, którą może mieć. Czasami struktura kodu może pomóc uniknąć wyjątku wskaźnika zerowego. np. podczas sprawdzania ciągu wejściowego ciągiem stałym powinieneś zacząć od ciągu ciągłego, jak tutaj: if („SomeString” .equals (inputString)) {} // nawet jeśli inputString ma wartość null, żaden wyjątek nie jest zgłaszany. Jest więc wiele rzeczy, które możesz zrobić, aby być bezpiecznym.
Rose

78
Dodatkowym sposobem uniknięcia NullPointerExceptionproblemów w kodzie jest użycie @Nullablei @NotNulladnotacje. Poniższa odpowiedź zawiera więcej informacji na ten temat. Chociaż ta odpowiedź dotyczy w szczególności IntelliJ IDE, ma ona również zastosowanie do innych narzędzi, podobnie jak przyrząd z komentarzy. (BTW Nie mogę bezpośrednio edytować tej odpowiedzi, być może autor może ją dodać?)
Arjan Mels

879

NullPointerExceptions są wyjątkami występującymi, gdy próbujesz użyć odwołania, które wskazuje na brak położenia w pamięci (null), tak jakby odnosiło się do obiektu. Wywołanie metody w odwołaniu zerowym lub próba dostępu do pola odwołania zerowego spowoduje wywołanie metody NullPointerException. Są to najczęściej, ale inne sposoby są wymienione na stronie NullPointerExceptionjavadoc.

Prawdopodobnie najszybszym przykładowym kodem, jaki mogłem wymyślić w celu zilustrowania, NullPointerExceptionbyłoby:

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

W pierwszym wierszu w środku mainwyraźnie ustawiam wartość Objectodniesienia objrówną null. Oznacza to, że mam odniesienie, ale nie wskazuje ono żadnego obiektu. Następnie staram się traktować referencję tak, jakby wskazywała na obiekt, wywołując na nim metodę. Powoduje to, NullPointerExceptionponieważ w miejscu wskazywanym przez odwołanie nie ma kodu do wykonania.

(Jest to technika, ale myślę, że należy wspomnieć: Odwołanie wskazujące na null nie jest tym samym co wskaźnik C wskazujący na nieprawidłowe położenie pamięci. Wskaźnik null dosłownie nie wskazuje nigdzie , co jest subtelnie różne niż wskazując lokalizację, która jest nieprawidłowa).


49
Zrozumiałem wszystko, co tam napisałeś, ale tylko dlatego, że pisałem od jakiegoś czasu i wiem, co to jest „wskaźnik” i „odniesienie” (i co to jest zero, jeśli o to chodzi). Kiedy próbuję zagłębić się w takie wyjaśnienia, moi uczniowie patrzą na mnie ze skrzyżowanymi rękami, ponieważ nie ma wystarczającego tła.
mmr

33
@mmr: Dziękujemy za opinię, robisz ważny punkt. W Internecie trudno jest naprawdę ocenić, gdzie jest ktoś, i na jakim poziomie bezpiecznie zacząć wyjaśnienie. Spróbuję to jeszcze raz poprawić.
Bill the Lizard

22
Bardziej powszechnym sposobem na uzyskanie NullPointerException w praktyce byłoby zapomnienie o jawnej inicjalizacji zmiennej członka na coś innego niż nullprzed użyciem jej, jak to . W przypadku zmiennych lokalnych kompilator wychwytuje ten błąd, ale w tym przypadku nie. Może to byłby użyteczny dodatek do twojej odpowiedzi?
Ilmari Karonen

6
@EJP Myślę, że twoje punkty są ważne, więc zaktualizowałem odpowiedź, aby była jaśniejsza i aby uniknąć mówienia „punkty do zera” tam, gdzie to się stało.
Steve Powell,

5
@ StevePowell Dawno temu powiedziałem, że nie chcę, aby moja odpowiedź uległa zmianie. Proszę uszanować intencję oryginalnego autora.
Bill the Lizard

696

Co to jest wyjątek NullPointerException?

Dobrym miejscem do rozpoczęcia jest JavaDocs . Obejmują to:

Zgłaszane, gdy aplikacja próbuje użyć wartości NULL w przypadku, gdy wymagany jest obiekt. Obejmują one:

  • Wywołanie metody wystąpienia obiektu zerowego.
  • Dostęp do lub modyfikowanie pola obiektu zerowego.
  • Przyjmuje długość null, jakby to była tablica.
  • Uzyskiwanie dostępu do szczelin zerowania lub modyfikowanie ich tak, jakby to była tablica.
  • Rzucanie zerowe, jakby to była wartość Throwable.

Aplikacje powinny zgłaszać instancje tej klasy, aby wskazać inne nielegalne użycie obiektu zerowego.

Jest tak również w przypadku, gdy próba użycia odwołania zerowego z synchronized, spowoduje również zgłoszenie tego wyjątku dla JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • W przeciwnym razie, jeśli wartość wyrażenia jest równa null, generowane NullPointerExceptionjest a .

Jak to naprawić?

Więc masz NullPointerException. Jak to naprawić? Weźmy prosty przykład, który rzuca NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Zidentyfikuj wartości zerowe

Pierwszym krokiem jest dokładne określenie, które wartości powodują wyjątek . W tym celu musimy przeprowadzić debugowanie. Ważne jest, aby nauczyć się czytać ślad stosu . To pokaże, gdzie został zgłoszony wyjątek:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Tutaj widzimy, że wyjątek jest zgłaszany w wierszu 13 (w printStringmetodzie). Spójrz na linię i sprawdź, które wartości są puste, dodając instrukcje rejestrowania lub używając debugera . Dowiadujemy się, że sjest to null, a wywołanie na nim lengthmetody zgłasza wyjątek. Widzimy, że program przestaje zgłaszać wyjątek, gdy s.length()zostanie usunięty z metody.

Śledź, skąd pochodzą te wartości

Następnie sprawdź, skąd pochodzi ta wartość. Postępując zgodnie z rozmówców sposobu, widzimy, że sjest przekazywana ze printString(name)w print()metodzie, a this.namejest null.

Śledź, gdzie należy ustawić te wartości

Gdzie jest this.nameustawiony? W setName(String)metodzie Po kolejnych debugowaniach możemy zobaczyć, że ta metoda w ogóle nie jest wywoływana. Jeśli metoda została wywołana, sprawdź kolejność wywoływania tych metod, a ustawiona metoda nie jest wywoływana po metodzie drukowania.

To wystarczy, aby dać nam rozwiązanie: dodaj połączenie printer.setName()przed, zanim zadzwonisz printer.print().

Inne poprawki

Zmienna może mieć wartość domyślną (i setNamemoże uniemożliwić jej ustawienie na null):

private String name = "";

Albo printalbo printStringmetoda może sprawdzić zerowy , na przykład:

printString((name == null) ? "" : name);

Lub możesz zaprojektować klasę, aby name zawsze miała wartość inną niż null :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

Zobacz też:

Nadal nie mogę znaleźć problemu

Jeśli próbujesz debugować problem i nadal nie masz rozwiązania, możesz wysłać pytanie, aby uzyskać dodatkową pomoc, ale pamiętaj, aby dołączyć to, co próbujesz do tej pory. Uwzględnij co najmniej stacktrace w pytaniu i zaznacz ważne numery wierszy w kodzie. Spróbuj także najpierw uprościć kod (patrz SSCCE ).


44
+1 Dobrze mieć przykład, który obejmuje przejście przez stacktrace; ważne jest, aby pokazać, dlaczego czytanie jest ważne przy debugowaniu NPE. (i dlaczego prawie zawsze szukamy stosu śledzenia, gdy ktoś opublikuje pytanie dotyczące błędu)
Dennis Meng

16
Wspomniałeś o debugowaniu ... Jak to działa? Od jakiegoś czasu badam ten temat, ale nic nie mogę znaleźć. Jestem pewien, że tak niesamowity nauczyciel jak ty możesz mnie tego nauczyć w ciągu sekundy! Dzięki wielkie! :-)
Ruchir Baronia,

15
@RuchirBaronia Debuger umożliwia przechodzenie przez program wiersz po wierszu, aby zobaczyć, które metody są wywoływane i jak zmieniane są zmienne. IDE powinny mieć do tego kilka narzędzi. Zobacz na przykład vogella.com/tutorials/EclipseDebugging/article.html .
fgb,

15
@RuchirBaronia Ustawiasz punkty przerwania w metodach wokół dowolnych wyjątków NullPointerException, jak widać w stacktrace, i porównujesz wartości zmiennych z oczekiwanymi. Jeśli wiesz, że zmienna ma wartość NULL, kiedy nie powinna, możesz ustawić punkty przerwania wokół dowolnego kodu, który zmienia wartość. Istnieją również warunkowe punkty przerwania, których możesz użyć, które powiedzą ci, kiedy zmienia się wartość.
fgb,

6
Ustawienie ciągów znaków na pusty ciąg znaków, ponieważ ich wartość domyślna jest uważana za złą praktykę.
Tiny

501

Pytanie: Co powoduje NullPointerException(NPE)?

Jak powinieneś wiedzieć, typy Java są podzielone na prymitywnych typów ( boolean, intetc.) i typów referencyjnych . Typy referencyjne w Javie pozwalają na użycie specjalnej wartości, nullktóra jest w Javie sposobem mówienia „brak obiektu”.

A NullPointerExceptionjest generowane w czasie wykonywania, ilekroć twój program próbuje użyć nulltak, jakby to było prawdziwe odniesienie. Na przykład, jeśli napiszesz to:

public class Test {
    public static void main(String[] args) {
        String foo = null;
        int length = foo.length();   // HERE
    }
}

instrukcja oznaczona „TUTAJ” będzie próbowała uruchomić length()metodę na nullreferencji, a to rzuci NullPointerException.

Istnieje wiele sposobów wykorzystania nullwartości, która spowoduje, że wartość NullPointerException. W rzeczywistości jedyne rzeczy, które możesz zrobić nullbez powodowania NPE to:

  • przypisać do zmiennej referencyjnej lub odczytać ze zmiennej referencyjnej,
  • przypisać go do elementu tablicy lub odczytać z elementu tablicy (pod warunkiem, że samo odwołanie do tablicy jest inne niż null!),
  • przekazać jako parametr lub zwrócić jako wynik, lub
  • przetestować go przy użyciu ==lub !=operatorów, lub instanceof.

Pytanie: Jak odczytać stos śledzenia NPE?

Załóżmy, że skompilowałem i uruchomiłem powyższy program:

$ javac Test.java 
$ java Test
Exception in thread "main" java.lang.NullPointerException
    at Test.main(Test.java:4)
$

Pierwsza obserwacja: kompilacja się udała! Problemem w programie NIE jest błąd kompilacji. Jest to błąd czasu wykonywania . (Niektóre IDE mogą ostrzegać, że Twój program zawsze zgłasza wyjątek ... ale standardowy javackompilator nie.)

Druga obserwacja: kiedy uruchamiam program, wypisuje on dwa wiersze „gobbledy-gook”. ŹLE!! To nie jest pożeracz. Jest to stacktrace ... i dostarcza istotnych informacji , które pomogą ci wyśledzić błąd w kodzie, jeśli poświęcisz trochę czasu na jego dokładne przeczytanie.

Spójrzmy więc na to, co mówi:

Exception in thread "main" java.lang.NullPointerException

Pierwszy wiersz śladu stosu mówi ci kilka rzeczy:

  • Podaje nazwę wątku Java, w którym został zgłoszony wyjątek. W przypadku prostego programu z jednym wątkiem (takim jak ten) będzie to „main”. Przejdźmy dalej ...
  • Podaje pełną nazwę zgłoszonego wyjątku; tj java.lang.NullPointerException.
  • Jeśli wyjątek ma powiązany komunikat o błędzie, zostanie wyświetlony po nazwie wyjątku. NullPointerExceptionjest pod tym względem niezwykły, ponieważ rzadko zawiera komunikat o błędzie.

Druga linia jest najważniejsza w diagnozowaniu NPE.

at Test.main(Test.java:4)

To mówi nam wiele rzeczy:

  • „at Test.main” mówi, że byliśmy w mainmetodzie Testklasy.
  • „Test.java:4” podaje źródłową nazwę pliku klasy ORAZ informuje, że instrukcja, w której to miało miejsce, znajduje się w wierszu 4 pliku.

Jeśli policzysz linie w powyższym pliku, linia 4 to ta, którą oznaczyłem komentarzem „TUTAJ”.

Zauważ, że w bardziej skomplikowanym przykładzie w śladzie stosu NPE będzie wiele linii. Ale możesz być pewien, że druga linia (pierwsza linia „w”) powie ci, gdzie został rzucony NPE 1 .

W skrócie, ślad stosu jednoznacznie powie nam, która instrukcja programu wyrzuciła NPE.

1 - Niezupełnie prawda. Są rzeczy zwane wyjątkami zagnieżdżonymi ...

Pytanie: Jak wyśledzić przyczynę wyjątku NPE w moim kodzie?

To jest najtrudniejsza część. Krótka odpowiedź polega na zastosowaniu logicznego wnioskowania do dowodów dostarczonych przez ślad stosu, kod źródłowy i odpowiednią dokumentację API.

Zilustrujmy najpierw prostym przykładem (powyżej). Zaczynamy od spojrzenia na wiersz, który powiedział nam ślad stosu, gdzie jest miejsce NPE:

int length = foo.length(); // HERE

Jak może to rzucić NPE?

W rzeczywistości istnieje tylko jeden sposób: może się to zdarzyć tylko wtedy, gdy fooma wartość null. Następnie próbujemy uruchomić length()metodę nulli ... BANG!

Ale (słyszę, jak mówisz) co się stanie, jeśli NPE zostanie wrzucony do length()wywołania metody?

Gdyby tak się stało, ślad stosu wyglądałby inaczej. Pierwsza linia „at” oznaczałaby, że wyjątek został zgłoszony w pewnej linii w java.lang.Stringklasie, a linia 4 Test.javabyłaby drugą linią „at”.

Więc skąd to się nullwzięło? W tym przypadku jest to oczywiste i oczywiste jest, co musimy zrobić, aby to naprawić. (Przypisz wartość inną niż null do foo.)

OK, spróbujmy więc nieco trudniejszego przykładu. Będzie to wymagało logicznej dedukcji .

public class Test {

    private static String[] foo = new String[2];

    private static int test(String[] bar, int pos) {
        return bar[pos].length();
    }

    public static void main(String[] args) {
        int length = test(foo, 1);
    }
}

$ javac Test.java 
$ java Test
Exception in thread "main" java.lang.NullPointerException
    at Test.test(Test.java:6)
    at Test.main(Test.java:10)
$ 

Więc teraz mamy dwie linie „at”. Pierwszy dotyczy tej linii:

return args[pos].length();

a drugi dotyczy tej linii:

int length = test(foo, 1);

Patrząc na pierwszą linię, w jaki sposób może to rzucić NPE? Istnieją dwa sposoby:

  • Jeśli wartość barjest, nullto bar[pos]wyrzuci NPE.
  • Jeśli wartość parametru bar[pos]to nullwywołanie length()spowoduje wygenerowanie NPE.

Następnie musimy dowiedzieć się, który z tych scenariuszy wyjaśnia, co się faktycznie dzieje. Zaczniemy od zbadania pierwszego:

Skąd barpochodzi? Jest to parametr testwywołania metody, a jeśli spojrzymy na to, jak testzostało wywołane, możemy zobaczyć, że pochodzi on od foozmiennej statycznej. Ponadto wyraźnie widać, że zainicjowaliśmy wartość fooinną niż zero. To wystarczy, aby wstępnie odrzucić to wyjaśnienie. (Teoretycznie coś innego może zmienić się foo na null... ale tak się nie dzieje.)

A co z naszym drugim scenariuszem? Cóż, możemy zobaczyć, że posjest 1, to znaczy, że tak foo[1]musi być null. czy to możliwe?

Rzeczywiście jest! I to jest problem. Kiedy inicjalizujemy w ten sposób:

private static String[] foo = new String[2];

alokujemy String[]z dwoma elementami, które są inicjowanenull . Po tym nie zmieniliśmy zawartości foo... i tak foo[1]pozostanie null.


425

To tak, jakbyś próbował uzyskać dostęp do obiektu, który jest null. Rozważ poniższy przykład:

TypeA objA;

W tej chwili właśnie zadeklarowałeś ten obiekt, ale nie został on zainicjowany ani utworzony . I za każdym razem, gdy spróbujesz uzyskać dostęp do dowolnej właściwości lub metody, rzut będzie miał NullPointerExceptionsens.

Zobacz także poniższy przykład:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown

1
Jeśli podamy System.out.println (a.length ()); // Zostanie zgłoszony wyjątek NullPointerException, aby pominąć ten problem, możemy poradzić sobie z blokiem try catch. dziękuję
Vijaya Varma Lanke

359

Wyjątek wskaźnika zerowego jest zgłaszany, gdy aplikacja próbuje użyć wartości null w przypadku, gdy wymagany jest obiekt. Obejmują one:

  1. Wywołanie metody wystąpienia nullobiektu.
  2. Dostęp lub modyfikacja pola nullobiektu.
  3. Przyjmuje długość nulltak, jakby to była tablica.
  4. Uzyskiwanie dostępu lub modyfikowanie gniazd nulltak, jakby to była tablica.
  5. Rzucanie, nulljakby to była wartość Throwable.

Aplikacje powinny zgłaszać instancje tej klasy w celu wskazania innych nielegalnych zastosowań nullobiektu.

Odniesienie: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html


12
Upraszczaj, podoba mi się ta odpowiedź, dodaj ją, jeśli uważasz, że jest poprawna - Dostęp do niezainicjowanego atrybutu obiektu
Emiliano

5
@Emiliano - sam dostęp do zainicjowanego atrybutu nie powoduje NPE. To, co >> robisz << z niezainicjowaną wartością atrybutu, powoduje NPE.
Stephen C

1
Jeśli chcesz więcej przypadków: 1) użycie a nulljako celu synchronizedbloku, 2) użycie a nulljako celu a switchi rozpakowanie null.
Stephen C

333

nullWskaźnik jest, że punkty do nikąd. Kiedy wyrenderujesz wskaźnik p, mówisz „daj mi dane w lokalizacji zapisanej w„ p ”. Kiedy pjest nullwskaźnik, lokalizacją w pjest nowhere, mówisz„ daj mi dane w lokalizacji „nigdzie” ”. Oczywiście nie może tego zrobić, więc rzuca null pointer exception.

Zasadniczo dzieje się tak, ponieważ coś nie zostało poprawnie zainicjowane.


2
Czy tworzymy bazę danych? -> NULLjest napisane jak nullw Javie. I jest to sprawa uwzględniająca wielkość liter.
bvdb,

3
„Wskaźnik NULL to taki, który wskazuje do nikąd” Nie zgadzam się. Wskaźniki zerowe nie wskazują nigdzie, wskazują na wartości zerowe.
TheRealChx101

2
@ TheRealChx101 Wskaźnik zerowy i wskaźnik do wartości zerowej to różne rzeczy - wskaźnik zerowy nie wskazuje wartości zerowej. Załóżmy, że masz wskaźnik do wskaźnika: wskaźnik A wskazuje wskaźnik B, a wskaźnik B ma wartość NULL. W takim przypadku wskaźnik A wskazuje na wartość zerową, a wskaźnik B jest wskaźnikiem zerowym.
MrZebra

321

Istnieje wiele wyjaśnień, aby wyjaśnić, jak to się dzieje i jak to naprawić, ale należy również postępować zgodnie z najlepszymi praktykami, aby NullPointerExceptionw ogóle uniknąć s.

Zobacz także: Dobra lista najlepszych praktyk

Chciałbym dodać, bardzo ważne, aby dobrze wykorzystać finalmodyfikator. Używanie modyfikatora „końcowego”, jeśli dotyczy Java

Podsumowanie:

  1. Użyj finalmodyfikatora, aby wymusić dobrą inicjalizację.
  2. Unikaj zwracania wartości null w metodach, na przykład zwracania pustych kolekcji, jeśli ma to zastosowanie.
  3. Użyj adnotacji @NotNulli@Nullable
  4. Szybko zawiedzie i użyj zapewnień, aby uniknąć propagacji zerowych obiektów w całej aplikacji, gdy nie powinny być zerowe.
  5. Najpierw użyj znaku równości ze znanym obiektem: if("knownObject".equals(unknownObject)
  6. Wolę valueOf()ponad toString().
  7. Użyj bezpiecznych StringUtilsmetod zerowania StringUtils.isEmpty(null).
  8. Użyj Java 8 Optional jako wartości zwracanej w metodach, klasa Optional zapewnia rozwiązanie do reprezentowania wartości opcjonalnych zamiast odwołań zerowych.

4
W projektach j2ee wyjątek Nullpointer jest bardzo częsty. W niektórych przypadkach zmienne referencyjne mają wartości zerowe, więc powinieneś poprawnie sprawdzić inicjalizację zmiennej. A podczas instrukcji warunkowej zawsze powinieneś sprawdzić, czy flaga lub referencja zawiera null, czy nie: - if (flag! = 0) {
Twój

14
Warto wspomnieć, że niektóre środowiska IDE (np. Eclipse) oferują automatyczną analizę nieważności na podstawie dostosowywanych adnotacji (np @Nullable. Wymienionych powyżej) i ostrzegają przed potencjalnymi błędami. Możliwe jest również wnioskowanie i generowanie takich adnotacji (np. IntelliJ może to zrobić) w oparciu o istniejącą strukturę kodu.
Jan Chimiak,

4
Pierwszą rzeczą, którą należy zrobić, to przed użyciem zerowalnego obiektu, powinieneś sprawdzić, czy jest to null, używając if (obj==null).Jeśli jest to null, powinieneś napisać kod, aby to również obsłużyć.
Lakmal Vithanage

4
IMO lepiej jest unikać zwracania obiektów pustych w metodach, gdy jest to możliwe, i używać adnotacji, gdy parametry wejściowe o wartości zerowej są niedozwolone, aby umownie zmniejszyć ilość „if (obj == null)” w kodzie i poprawić czytelność kodu.
LG

4
Przeczytaj to ... zanim zaakceptujesz te „najlepsze praktyki” jako prawdę: satysfakcja.com/blog/archives/27
Stephen C

316

W Javie wszystko (z wyjątkiem typów pierwotnych) ma postać klasy.

Jeśli chcesz użyć dowolnego obiektu, masz dwie fazy:

  1. Ogłosić
  2. Inicjalizacja

Przykład:

  • Deklaracja: Object object;
  • Inicjalizacja: object = new Object();

To samo dotyczy koncepcji tablicy:

  • Deklaracja: Item item[] = new Item[5];
  • Inicjalizacja: item[0] = new Item();

Jeśli nie podajesz sekcji inicjalizacyjnej, NullPointerExceptionpowstają.


3
Podczas wywoływania metody instancji często występuje wyjątek NullPointerException. Na przykład, jeśli zadeklarujesz odwołanie, ale nie wskaże ono żadnej instancji, wystąpi wyjątek NullPointerException po wywołaniu jego metody. takie jak: YourClass ref = null; // lub ref = anotherRef; // ale anotherRef nie wskazał żadnej instancji ref.someMethod (); // wyrzuci wyjątek NullPointerException. Zasadniczo napraw to w ten sposób: Przed wywołaniem metody sprawdź, czy odwołanie ma wartość NULL. takie jak: if (yourRef! = null) {yourRef.someMethod (); }
sunhang

2
Lub użyj przechwytywania wyjątków: na przykład: try {yourRef.someMethod (); } catch (NullPointerException e) {// TODO}
sunhang


315

Wyjątkiem wskaźnika zerowego jest wskaźnik używania obiektu bez inicjowania go.

Na przykład poniżej znajduje się klasa ucznia, która wykorzysta ją w naszym kodzie.

public class Student {

    private int id;

    public int getId() {
        return this.id;
    }

    public setId(int newId) {
        this.id = newId;
    }
}

Poniższy kod zawiera wyjątek wskaźnika zerowego.

public class School {

    Student student;

    public School() {
        try {
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}

Ponieważ używasz student, ale zapomniałeś zainicjować go tak jak w poprawnym kodzie pokazanym poniżej:

public class School {

    Student student;

    public School() {
        try {
            student = new Student();
            student.setId(12);
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}

7
Chociaż jest to dobry przykład, czy mogę zadać pytanie, które dodaje do pytania, które nie zostało jeszcze uwzględnione we wszystkich innych odpowiedziach?
Mysticial

13
Używanie tutaj słowa „niezainicjowany” jest po prostu niewłaściwe. Pokazany przykład jest w rzeczywistości „inicjowany” i jest inicjowany na zero. W przypadku niezainicjowanych zmiennych kompilator będzie narzekał na Ciebie.
Adrian Shum,

2
NPE może wskazywać, że używasz niezainicjowanego pola. Może to wskazywać, że robisz inne rzeczy. Nadmierne uproszczenie do jednej takiej przyczyny nie pomaga komuś rozwiązać problemów NPE ... jeśli rzeczywista przyczyna nie jest taka.
Stephen C

309

W Javie wszystkie deklarowane zmienne są w rzeczywistości „odwołaniami” do obiektów (lub prymitywów), a nie do samych obiektów.

Podczas próby wykonania jednej metody obiektowej odwołanie prosi żywy obiekt o wykonanie tej metody. Ale jeśli odwołanie odwołuje się do NULL (nic, zero, void, nada), wówczas nie ma możliwości wykonania metody. Następnie środowisko wykonawcze informuje o tym, zgłaszając wyjątek NullPointerException.

Twoje odwołanie wskazuje „null”, a zatem „Null -> Pointer”.

Obiekt żyje w przestrzeni pamięci maszyny wirtualnej, a jedynym sposobem na dostęp do niego jest użycie thisreferencji. Weź ten przykład:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

I w innym miejscu w kodzie:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

To ważne, aby wiedzieć - kiedy nie ma już odniesienia do obiektu (w powyższym przykładzie, kiedy referencei otherReferencejak punkt null) następnie przedmiotem jest „nieosiągalny”. Nie ma sposobu, abyśmy mogli z nim pracować, więc ten obiekt jest gotowy do wyrzucania elementów bezużytecznych, aw pewnym momencie maszyna wirtualna zwolni pamięć używaną przez ten obiekt i przydzieli inny.


280

Kolejne zjawisko NullPointerExceptionwystępuje, gdy deklaruje się tablicę obiektów, a następnie natychmiast próbuje się od niej oddzielić elementy.

String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

Tego konkretnego NPE można uniknąć, jeśli kolejność porównania zostanie odwrócona; mianowicie użycie .equalsna gwarantowanym obiekcie innym niż null.

Wszystkie elementy wewnątrz tablicy są inicjowane do wspólnej wartości początkowej ; dla dowolnego typu tablicy obiektowej oznacza to, że wszystkie elementy sąnull .

Państwo musi zainicjować elementów tablicy przed dostępem lub ich dereferencji.

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

operacja na niezainicjowanym obiekcie na poziomie instancji (nie na poziomie klasy) doprowadzi do NullPointerException. operacja musi być specyficzna dla instancji. jeśli operacja jest na poziomie klasy, mówiąc o wywołaniu metody statycznej na niezainicjowanym obiekcie, to nie zgłosi wyjątku NullPointerException. Nawet prymitywne obiekty klasy opakowania zgłaszają wyjątek NullPointerException.
Shailendra Singh,

1. NullPointerException to RuntimeException, co oznacza, że ​​pojawi się podczas działania programu, a nie w czasie kompilacji.! :(, ale większość IDE pomaga ci to odkryć. 2. Minimalizuj użycie słowa kluczowego „null” w instrukcjach przypisania. :) Adres URL:
tomj0101

@ tomj0101 Jestem całkowicie niejasny, dlaczego skomentowałeś ten komentarz ... Ale do twojego drugiego punktu, wcześniejszym wzorem Optionalbyło zwrócenie wartości zerowej. Słowo kluczowe jest w porządku. Umiejętność obrony przed tym jest niezwykle ważna. Zapewnia to jedno powszechne jego wystąpienie i sposoby jego złagodzenia.
Makoto,

NullPointerException to wyjątek w czasie wykonywania, który nie jest zalecany do przechwytywania go, ale unikania go.
Shomu

2
@Shomu: W którym momencie nawet sugeruję, że należy go złapać?
Makoto
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.