Odpowiedzi:
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
int a=bmogą rzucać NPE, jeśli b jest an Integer. Są przypadki, w których debugowanie jest mylące.
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ć?)
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).
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?
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 .
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().
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ż:
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 ).
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:
==lub !=operatorów, lub instanceof.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:
java.lang.NullPointerException.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:
mainmetodzie Testklasy.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 ...
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:
barjest, nullto bar[pos]wyrzuci NPE.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.
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
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:
nullobiektu.nullobiektu.nulltak, jakby to była tablica.nulltak, jakby to była tablica.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
nulljako celu synchronizedbloku, 2) użycie a nulljako celu a switchi rozpakowanie null.
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.
NULLjest napisane jak nullw 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 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:
finalmodyfikatora, aby wymusić dobrą inicjalizację.@NotNulli@Nullableif("knownObject".equals(unknownObject)valueOf()ponad toString().StringUtilsmetod zerowania StringUtils.isEmpty(null).@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, NullPointerExceptionpowstają.
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");
}
}
}
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.
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));
}
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.