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?
źródło
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?
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ą x
jest an, int
a Java zainicjuje ją 0
dla Ciebie. Kiedy przypisujesz mu wartość 10
w drugim wierszu, twoja wartość 10
jest 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 Integer
typem odniesienia). Ponieważ nie powiedziałeś jeszcze, na co ma wskazywać, Java ustawia to null
, co oznacza „ Nie wskazuję na nic ”.
W drugim wierszu new
słowo kluczowe służy do tworzenia (lub tworzenia) obiektu typu, Integer
a zmienna wskaźnikowa num
jest przypisana do tego Integer
obiektu.
NullPointerException
Wystę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ć num
PRZED 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 obj
jest null
. Jeśli metoda ma na celu zrobienie czegoś z przekazanym obiektem, należy zgłosić błąd, NullPointerException
ponieważ 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
int a=b
mogą rzucać NPE, jeśli b jest anInteger
. Są przypadki, w których debugowanie jest mylące.NullPointerException
problemów w kodzie jest użycie@Nullable
i@NotNull
adnotacje. 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ć?)NullPointerException
s 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 metodyNullPointerException
. Są to najczęściej, ale inne sposoby są wymienione na stronieNullPointerException
javadoc.Prawdopodobnie najszybszym przykładowym kodem, jaki mogłem wymyślić w celu zilustrowania,
NullPointerException
byłoby:W pierwszym wierszu w środku
main
wyraźnie ustawiam wartośćObject
odniesieniaobj
ró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,NullPointerException
ponieważ 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).
źródło
null
przed 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?Co to jest wyjątek NullPointerException?
Dobrym miejscem do rozpoczęcia jest JavaDocs . Obejmują to:
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 :Jak to naprawić?
Więc masz
NullPointerException
. Jak to naprawić? Weźmy prosty przykład, który rzucaNullPointerException
: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:
Tutaj widzimy, że wyjątek jest zgłaszany w wierszu 13 (w
printString
metodzie). Spójrz na linię i sprawdź, które wartości są puste, dodając instrukcje rejestrowania lub używając debugera . Dowiadujemy się, żes
jest to null, a wywołanie na nimlength
metody zgłasza wyjątek. Widzimy, że program przestaje zgłaszać wyjątek, gdys.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
s
jest przekazywana zeprintString(name)
wprint()
metodzie, athis.name
jest null.Śledź, gdzie należy ustawić te wartości
Gdzie jest
this.name
ustawiony? WsetName(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 zadzwoniszprinter.print()
.Inne poprawki
Zmienna może mieć wartość domyślną (i
setName
może uniemożliwić jej ustawienie na null):Albo
print
alboprintString
metoda może sprawdzić zerowy , na przykład:Lub możesz zaprojektować klasę, aby
name
zawsze miała wartość inną niż null :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 ).
źródło
Pytanie: Co powoduje
NullPointerException
(NPE)?Jak powinieneś wiedzieć, typy Java są podzielone na prymitywnych typów (
boolean
,int
etc.) i typów referencyjnych . Typy referencyjne w Javie pozwalają na użycie specjalnej wartości,null
która jest w Javie sposobem mówienia „brak obiektu”.A
NullPointerException
jest generowane w czasie wykonywania, ilekroć twój program próbuje użyćnull
tak, jakby to było prawdziwe odniesienie. Na przykład, jeśli napiszesz to:instrukcja oznaczona „TUTAJ” będzie próbowała uruchomić
length()
metodę nanull
referencji, a to rzuciNullPointerException
.Istnieje wiele sposobów wykorzystania
null
wartości, która spowoduje, że wartośćNullPointerException
. W rzeczywistości jedyne rzeczy, które możesz zrobićnull
bez powodowania NPE to:==
lub!=
operatorów, lubinstanceof
.Pytanie: Jak odczytać stos śledzenia NPE?
Załóżmy, że skompilowałem i uruchomiłem powyższy program:
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
javac
kompilator 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:
Pierwszy wiersz śladu stosu mówi ci kilka rzeczy:
java.lang.NullPointerException
.NullPointerException
jest pod tym względem niezwykły, ponieważ rzadko zawiera komunikat o błędzie.Druga linia jest najważniejsza w diagnozowaniu NPE.
To mówi nam wiele rzeczy:
main
metodzieTest
klasy.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:
Jak może to rzucić NPE?
W rzeczywistości istnieje tylko jeden sposób: może się to zdarzyć tylko wtedy, gdy
foo
ma wartośćnull
. Następnie próbujemy uruchomićlength()
metodęnull
i ... 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.String
klasie, a linia 4Test.java
byłaby drugą linią „at”.Więc skąd to się
null
wzięło? W tym przypadku jest to oczywiste i oczywiste jest, co musimy zrobić, aby to naprawić. (Przypisz wartość inną niż null dofoo
.)OK, spróbujmy więc nieco trudniejszego przykładu. Będzie to wymagało logicznej dedukcji .
Więc teraz mamy dwie linie „at”. Pierwszy dotyczy tej linii:
a drugi dotyczy tej linii:
Patrząc na pierwszą linię, w jaki sposób może to rzucić NPE? Istnieją dwa sposoby:
bar
jest,null
tobar[pos]
wyrzuci NPE.bar[pos]
tonull
wywołanielength()
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
bar
pochodzi? Jest to parametrtest
wywołania metody, a jeśli spojrzymy na to, jaktest
zostało wywołane, możemy zobaczyć, że pochodzi on odfoo
zmiennej statycznej. Ponadto wyraźnie widać, że zainicjowaliśmy wartośćfoo
inną niż zero. To wystarczy, aby wstępnie odrzucić to wyjaśnienie. (Teoretycznie coś innego może zmienić sięfoo
nanull
... ale tak się nie dzieje.)A co z naszym drugim scenariuszem? Cóż, możemy zobaczyć, że
pos
jest1
, to znaczy, że takfoo[1]
musi byćnull
. czy to możliwe?Rzeczywiście jest! I to jest problem. Kiedy inicjalizujemy w ten sposób:
alokujemy
String[]
z dwoma elementami, które są inicjowanenull
. Po tym nie zmieniliśmy zawartościfoo
... i takfoo[1]
pozostanienull
.źródło
To tak, jakbyś próbował uzyskać dostęp do obiektu, który jest
null
. Rozważ poniższy przykład: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ł
NullPointerException
sens.Zobacz także poniższy przykład:
źródło
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:
null
obiektu.null
obiektu.null
tak, jakby to była tablica.null
tak, jakby to była tablica.null
jakby to była wartość Throwable.Aplikacje powinny zgłaszać instancje tej klasy w celu wskazania innych nielegalnych zastosowań
null
obiektu.Odniesienie: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
źródło
null
jako celusynchronized
bloku, 2) użycie anull
jako celu aswitch
i rozpakowanienull
.null
Wskaźnik jest, że punkty do nikąd. Kiedy wyrenderujesz wskaźnikp
, mówisz „daj mi dane w lokalizacji zapisanej w„ p ”. Kiedyp
jestnull
wskaźnik, lokalizacją wp
jestnowhere
, mówisz„ daj mi dane w lokalizacji „nigdzie” ”. Oczywiście nie może tego zrobić, więc rzucanull pointer exception
.Zasadniczo dzieje się tak, ponieważ coś nie zostało poprawnie zainicjowane.
źródło
NULL
jest napisane jaknull
w Javie. I jest to sprawa uwzględniająca wielkość liter.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
NullPointerException
w ogóle uniknąć s.Zobacz także: Dobra lista najlepszych praktyk
Chciałbym dodać, bardzo ważne, aby dobrze wykorzystać
final
modyfikator. Używanie modyfikatora „końcowego”, jeśli dotyczy JavaPodsumowanie:
final
modyfikatora, aby wymusić dobrą inicjalizację.@NotNull
i@Nullable
if("knownObject".equals(unknownObject)
valueOf()
ponadtoString()
.StringUtils
metod zerowaniaStringUtils.isEmpty(null)
.źródło
@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.if (obj==null)
.Jeśli jest to null, powinieneś napisać kod, aby to również obsłużyć.W Javie wszystko (z wyjątkiem typów pierwotnych) ma postać klasy.
Jeśli chcesz użyć dowolnego obiektu, masz dwie fazy:
Przykład:
Object object;
object = new Object();
To samo dotyczy koncepcji tablicy:
Item item[] = new Item[5];
item[0] = new Item();
Jeśli nie podajesz sekcji inicjalizacyjnej,
NullPointerException
powstają.źródło
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.
Poniższy kod zawiera wyjątek wskaźnika zerowego.
Ponieważ używasz
student
, ale zapomniałeś zainicjować go tak jak w poprawnym kodzie pokazanym poniżej:źródło
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
this
referencji. Weź ten przykład:I w innym miejscu w kodzie:
To ważne, aby wiedzieć - kiedy nie ma już odniesienia do obiektu (w powyższym przykładzie, kiedy
reference
iotherReference
jak 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.źródło
Kolejne zjawisko
NullPointerException
występuje, gdy deklaruje się tablicę obiektów, a następnie natychmiast próbuje się od niej oddzielić elementy.Tego konkretnego NPE można uniknąć, jeśli kolejność porównania zostanie odwrócona; mianowicie użycie
.equals
na 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.
źródło
Optional
był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.