Was ist ein Nullpointer, und wie kann ich es beheben?
-
03-07-2019 - |
Frage
Was ist Null-Zeiger-Ausnahmen (java.lang.NullPointerException
) und was sie verursacht?
Welche Methoden / Geräte verwendet werden kann, um die Ursache zu ermitteln, so dass Sie die Ausnahme beenden Sie das Programm von verursacht vorzeitig zu beenden?
Lösung
Wenn Sie eine Referenzvariable deklarieren (das heißt ein Objekt) Sie wirklich einen Zeiger auf ein Objekt zu schaffen. Betrachten Sie den folgenden Code, in dem Sie eine Variable vom Urtyp int
deklarieren:
int x;
x = 10;
In diesem Beispiel wird die Variable x
ist ein int
und Java wird initialisieren es für Sie 0
. Wenn Sie den Wert von 10
in der zweiten Zeile zuweisen, Ihr Wert von 10
wird in den Speicherplatz geschrieben von x
bezeichnet.
Aber, wenn Sie versuchen, einen Verweis zu erklären type , etwas anderes passiert. Nehmen Sie den folgenden Code ein:
Integer num;
num = new Integer(10);
Die erste Zeile deklariert eine Variable num
genannt, aber es eigentlich keinen Grundwert noch enthalten. Stattdessen enthält sie einen Zeiger (weil der Typ, der eine Referenz Integer
Typ ist). Da Sie noch nicht gesagt, was zu zeigen, setzt Java es null
, was bedeutet, " Ich deutete auf nichts ".
In der zweiten Zeile wird das Schlüsselwort verwendet new
zu instanziiert (oder erstellen) ein Objekt des Typs Integer
und die Zeigervariable num
zu diesem Integer
Objekt zugeordnet.
Die NullPointerException
tritt auf, wenn Sie eine Variable deklarieren, aber nicht schaffen, ein Objekt. So sind Sie zeigt auf etwas, das nicht wirklich existiert.
Wenn Sie zu dereferenzieren num
versuchen, bevor Sie das Objekt erstellen erhalten Sie einen NullPointerException
. In den trivialen Fällen wird der Compiler das Problem fangen und lassen Sie wissen, dass „num may not have been initialized
“, aber manchmal können Sie Code schreiben, der nicht direkt das Objekt nicht erstellen.
Zum Beispiel können Sie eine Methode wie folgt:
public void doSomething(SomeObject obj) {
//do something to obj
}
In diesem Fall, erstellen Sie das Objekt nicht obj
, sondern vielmehr davon aus, dass es erstellt wurde, bevor die doSomething()
Methode aufgerufen wurde. Beachten Sie, es ist möglich, das Verfahren so zu nennen:
doSomething(null);
In diesem Fall ist obj
null
. Wenn das Verfahren soll etwas zum übergebene Objekt tun, ist es angebracht, die NullPointerException
zu werfen, weil es ein Programmierer Fehler ist und der Programmierer, dass die Informationen für Debugging-Zwecke benötigen.
Alternativ kann es Fälle geben, in denen der Zweck des Verfahrens nicht nur auf dem übergebenen Objekt zu bedienen ist und daher ein Nullparameter akzeptabel sein kann. In diesem Fall müssten Sie für eine null Parameter und verhalten sich anders zu überprüfen. Sie sollten auch diese in der Dokumentation erklären. Zum Beispiel doSomething()
geschrieben werden als:
/**
* @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
}
}
Schließlich Wie die Ausnahme lokalisieren und verursachen mit Stack-Trace
Andere Tipps
NullPointerException
s sind Ausnahmen, die auftreten, wenn Sie versuchen, eine Referenz zu verwenden, die im Speicher (null) zu keinem Ort verweist, als ob es ein Objekt wurde verweisen. Aufruf einer Methode auf einem NULL-Verweis oder versuchen, ein Feld eines NULL-Verweis für den Zugriff auf eine NullPointerException
auslösen. Dies sind die häufigsten, aber auch andere Möglichkeiten, auf der NullPointerException
javadoc Seite.
Wahrscheinlich der schnellste Beispiel Code, den ich einfiel eine NullPointerException
zu erläutern sei:
public class Example {
public static void main(String[] args) {
Object obj = null;
obj.hashCode();
}
}
In der ersten Zeile innerhalb main
, bin ich Einstellung explizit die Object
Referenz obj
gleich null
. Das bedeutet, ich habe eine Referenz, aber es zeigt nicht auf ein beliebiges Objekt. Danach habe ich versuchen, die Referenz zu behandeln, als ob es durch den Aufruf einer Methode auf es auf ein Objekt verweist. Dies führt zu einem NullPointerException
, weil es kein Code ist in der Lage auszuführen, die die Referenz verweist.
(Dies ist eine Form, aber ich denke, es trägt zu erwähnen. Eine Referenz, die auf null Punkte ist nicht das gleiche wie ein C-Zeiger, der auf eine ungültige Speicherstelle weist ein Null-Zeiger ist buchstäblich zeigt nicht überall , die auf subtile Weise anders ist zu einer Stelle als Hinweis, das ungültig sein geschieht.)
Was ist ein Nullpointer?
Ein guter Anfang ist die JavaDocs . Sie haben dies behandelt:
Geworfen, wenn eine Anwendung versucht, in einem Fall null zu verwenden, wo ein Objekt erforderlich ist. Dazu gehören:
- Der Aufruf der Instanzmethode eines Null-Objekt.
- Zugriff oder das Feld eines Null-Objekt zu ändern.
- Nehmen Sie die Länge von null, als ob es ein Array waren.
- Zugriff oder die Schlitze null Modifizieren, als ob es ein Array waren.
- Werfen null, als ob es ein Throwable Wert war.
Die Anträge sollten Instanzen dieser Klasse werfen andere, um anzuzeigen, illegale Verwendungen des Null-Objekt.
Es ist auch der Fall, dass, wenn Sie versuchen, einen NULL-Verweis mit synchronized
zu verwenden, die auch diese Ausnahme werfen, pro der JLS :
SynchronizedStatement: synchronized ( Expression ) Block
- Andernfalls, wenn der Wert des Ausdrucks Null ist, wird ein
NullPointerException
geworfen.
Wie kann ich das Problem beheben?
So haben Sie eine NullPointerException
haben. Wie sehen Sie das Problem beheben? Nehmen wir ein einfaches Beispiel nehmen, die eine NullPointerException
wirft:
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();
}
}
Identifizieren Sie die NULL-Werte
Der erste Schritt ist das Identifizieren genau welche Werte verursachen die Ausnahme . Dafür brauchen wir einige Debug zu tun. Es ist wichtig, ein stacktrace , lesen zu lernen. Dies wird Ihnen zeigen, wo die Ausnahme ausgelöst wurde:
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)
Hier sehen wir, dass die Ausnahme auf der Leitung geworfen 13 (in der printString
Methode). Schauen Sie sich die Linie und prüfen Sie, welche Werte sind null durch
Hinzufügen Logging-Anweisungen oder Debugger verwenden. Wir finden heraus, dass s
null ist, und auf die length
Aufruf der Methode wirft die Ausnahme. Wir können sehen, dass das Programm nicht mehr die Ausnahme werfen, wenn s.length()
aus dem Verfahren entfernt wird.
Trace, wo diese Werte stammen aus
Als nächstes Check wo dieser Wert herkommt. Indem Sie den Anrufer des Verfahrens sehen wir, dass s
mit printString(name)
im print()
Verfahren eingeleitet, und this.name
ist null.
Trace wo sollten diese Werte eingestellt werden
Wo ist this.name
Satz? Im setName(String)
Verfahren. Mit etwas mehr Debuggen, können wir sehen, dass diese Methode nicht aufgerufen wird. Wenn die Methode aufgerufen wurde, stellen Sie sicher, dass die um zu prüfen, , dass diese Methoden genannt werden, und die Set-Methode ist nicht genannt nach der Druckmethode.
Das ist genug, um uns um eine Lösung zu geben: einen Anruf vor dem Aufruf printer.setName()
printer.print()
hinzufügen
Andere Korrekturen
Die Variable kann einen Standardwert (und setName
kann verhindern, dass es auf null gesetzt wird):
private String name = "";
Entweder die print
oder printString
Methode kann für null überprüfen , zum Beispiel:
printString((name == null) ? "" : name);
Sie können auch die Klasse so auszugestalten, dass name
immer einen Nicht-Null-Wert :
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();
}
}
Siehe auch:
Ich kann immer noch nicht das Problem finden
Wenn Sie das Problem zu debuggen, versucht, und haben immer noch keine Lösung, können Sie eine Frage, um weitere Hilfe zu veröffentlichen, aber stellen Sie sicher zu schließen, was Sie bisher versucht haben. Zumindest sind die Stacktrace in der Frage, und kennzeichnen die wichtigen Zeilennummern im Code. Versuchen Sie auch, den Code zu vereinfachen ersten (siehe SSCCE ).
Frage: Was bewirkt ein NullPointerException
(NPE)
?
Wie Sie wissen sollten, sind Java-Typen unterteilt in primitive Typen (boolean
, int
, etc.) und Referenztypen . Referenztypen in Java ermöglicht es Ihnen, den besonderen Wert null
zu verwenden, das ist die Java-Weg „kein Objekt“ zu sagen.
Ein NullPointerException
wird zur Laufzeit geworfen, wenn Ihr Programm versucht, eine null
zu verwenden, als ob es eine echte Referenz war. Zum Beispiel, wenn Sie schreiben diese:
public class Test {
public static void main(String[] args) {
String foo = null;
int length = foo.length(); // HERE
}
}
die Aussage mit „HIER“ wird die length()
Methode auf einer null
Referenz, um zu versuchen zu laufen, und dies wird ein NullPointerException
werfen.
Es gibt viele Möglichkeiten, wie Sie einen null
Wert verwenden könnte, die in einem NullPointerException
führen. In der Tat, dass die einzigen Dinge, die Sie können zu tun mit einem null
ohne eine NPE zu verursachen, sind:
- Zuweisen zu einem Referenzgröße oder diese von einer Referenzgröße lesen,
- Zuweisen zu einem Array-Elemente oder aus einem Array-Elemente gelesen werden (vorausgesetzt, dass Array Referenz selbst nicht null!),
- es als Parameter übergeben oder als Ergebnis zurück, oder
- test es die
==
oder!=
Operatoren oderinstanceof
.
Frage: Wie kann ich die NPE stacktrace lesen
Angenommen, dass ich kompilieren und starten Sie das Programm über:
$ javac Test.java
$ java Test
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:4)
$
Erste Beobachtung: die Kompilierung erfolgreich ist! Das Problem in dem Programm ist nicht ein Übersetzungsfehler. Es ist ein Laufzeit Fehler. (Einige IDEs kann Ihr Programm warnen immer eine Ausnahme werfen ... aber der Standard javac
Compiler nicht.)
Zweite Beobachtung: Wenn ich das Programm ausführen, es gibt zwei Linien von „gobbledy-Gook“. FALSCH !! Das ist nicht gobbledy-gook. Es ist ein stacktrace ... und es bietet wichtige Informationen , die Sie den Fehler in Ihrem Code aufzuspüren helfen, wenn Sie die Zeit nehmen, sie sorgfältig zu lesen.
also lasst uns schauen, was es sagt:
Exception in thread "main" java.lang.NullPointerException
Die erste Zeile des Stack-Trace zeigt Ihnen eine Reihe von Dingen:
- Es sagt Ihnen, den Namen des Java-Thread, in dem die Ausnahme ausgelöst wurde. Für ein einfaches Programm mit einem Thread (wie diese), wird es „main“ sein. Lassen Sie uns weitermachen ...
- Es sagt Ihnen, den vollständigen Namen der Ausnahme, die ausgelöst wurde; das heißt
java.lang.NullPointerException
. - Wenn die Ausnahme eine zugehörige Fehlermeldung hat, das wird ausgegeben, nachdem Ausnahme Name sein.
NullPointerException
ist ungewöhnlich, in dieser Hinsicht, weil es selten eine Fehlermeldung hat.
Die zweite Zeile ist das wichtigste eine NPE bei der Diagnose.
at Test.main(Test.java:4)
Dies sagt uns, eine Reihe von Dingen:
- "bei Test.main" sagt, dass wir in der
main
Methode derTest
Klasse sind. - „Test.java:4“ gibt die Quelle Dateinamen der Klasse, und es sagt uns, dass die Aussage, wo diese in Zeile aufgetreten ist 4 der Datei.
Wenn Sie die Zeilen in der Datei oben zählen, Zeile 4 ist die eine, die ich mit dem „HERE“ Kommentar gekennzeichnet.
Beachten Sie, dass in einem komplizierteren Beispiel wird es viele Linien in dem NPE-Stack-Trace sein. Aber Sie können sicher sein, dass die zweite Zeile (die erste „auf“ Linie) werden Ihnen sagen, wo der NPE geworfen wurde 1 .
Kurzschluss in dem Stack-Trace wird uns sagen eindeutig, welche Aussage des Programms der NPE geworfen hat.
1 - Nicht ganz richtig. Es gibt Dinge, verschachtelte Ausnahmen genannt ...
Frage: Wie kann ich die Ursache für die NPE Ausnahme in meinem Code aufzuspüren
Dies ist der schwierige Teil. Die kurze Antwort ist logische Folgerung zu den Beweismittel durch den Stack-Trace, der Quellcode und die entsprechenden API-Dokumentation.
vorgesehen anzuwendenLassen Sie sich mit dem einfachen Beispiel illustriert (siehe oben) zuerst. Wir beginnen am l suchenine, dass der Stack-Trace hat uns gesagt, wo der NPE passiert:
int length = foo.length(); // HERE
Wie kann das eine NPE werfen?
In der Tat gibt es nur einen Weg: es kann nur geschehen, wenn foo
den Wert null
hat. Wir haben dann versuchen, die length()
Methode auf null
laufen und .... BANG!
Aber (höre ich Sie sagen), was, wenn die NPE in dem length()
Methodenaufruf ausgelöst wurde?
Nun, wenn das passiert, würde die Stack-Trace anders aussehen. Die erste „auf“ Linie würde sagen, dass die Ausnahme in irgendeiner Zeile in der java.lang.String
Klasse geworfen wurde, und Linie 4 von Test.java
wäre die zweite „auf“ Linie sein.
Also, wo ist die null
gekommen? In diesem Fall ist es offensichtlich, und es ist offensichtlich, was wir es beheben tun müssen. (Vergeben Sie einen Nicht-Null-Wert zu foo
.)
OK, so wollen sie versuchen, ein etwas kniffliges Beispiel. Dies wird einige erfordern logische Deduktion .
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)
$
So, jetzt haben wir zwei „auf“ Linien. Die erste ist für diese Linie:
return args[pos].length();
und die zweite ist für diese Linie:
int length = test(foo, 1);
Mit Blick auf der ersten Zeile, wie das eine NPE werfen könnte? Es gibt zwei Möglichkeiten:
- Wenn der Wert von
bar
istnull
dannbar[pos]
eine NPE werfen. - Wenn der Wert von
bar[pos]
wirdnull
dannlength()
fordert sie auf, wird eine NPE werfen.
Als nächstes müssen wir, um herauszufinden, welche dieser Szenarien erklärt, was tatsächlich geschieht. Wir werden zunächst die erste Erkundung:
Wo kommt bar
kommen aus? Es ist ein Parameter an den test
Methodenaufruf, und wenn wir uns anschauen, wie test
genannt wurde, können wir sehen, dass es von den foo
statischen Variablen kommt. Darüber hinaus können wir deutlich sehen, dass wir foo
auf einen Wert ungleich Null initialisiert. Das reicht aus, um versuchsweise diese Erklärung zu entlassen. (In der Theorie etwas anderes könnte Veränderung foo
null
... aber das geschieht nicht hier.)
Und was ist unser zweites Szenario? Nun, wir können sehen, dass pos
1
ist, so bedeutet das, dass foo[1]
null
werden müssen. Ist das möglich?
Tatsächlich ist es! Und das ist das Problem. Wenn wir initialisieren wie folgt aus:
private static String[] foo = new String[2];
Wir weisen Sie einen String[]
mit zwei Elementen , die null
initialisiert . Danach haben wir den Inhalt foo
nicht geändert ... so foo[1]
noch null
werden.
Es ist wie Sie versuchen, auf ein Objekt zuzugreifen, die null
ist. Betrachten wir folgende Beispiel:
TypeA objA;
Zu diesem Zeitpunkt müssen Sie nur noch erklärt , um dieses Objekt aber nicht initialisiert oder instanziiert . Und wenn Sie versuchen, in das jede Eigenschaft oder Methode zuzugreifen, wird es NullPointerException
werfen, die Sinn macht.
Sehen Sie das folgende Beispiel auch:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
Eine Null-Zeiger-Ausnahme wird ausgelöst, wenn eine Anwendung in einem Fall null zu verwenden versucht, in dem ein Objekt erforderlich ist. Dazu gehören:
- Aufruf der Instanzmethode eines
null
Objekt. - Zugriff oder das Feld eines
null
Objekt zu ändern. - Nehmen Sie die Länge von
null
, als ob es ein Array waren. - Zugriff oder die Schlitze
null
modifizieren, als ob es ein Array waren. - Werfen
null
, als ob es ein Throwable Wert war.
Die Anträge sollten Instanzen dieser Klasse werfen, um andere illegale Verwendungen des null
Objekts.
Referenz: http://docs.oracle. com / JavaSE / 8 / docs / api / java / lang / NullPointerException.html
Ein null
Zeiger ist eine, die nirgendwo verweist. Wenn Sie dereferenzieren einen Zeiger p
, Sie sagen, „geben Sie mir die Daten an der Stelle in gespeichert‚p‘. Wenn p
ein null
Zeiger gespeicherte Position in p
nowhere
ist, da sagst du:“ Gib mir die Daten an der Stelle ‚nirgendwo‘“. Offensichtlich kann sie dies nicht tun, so dass es eine null pointer exception
wirft.
In der Regel ist es weil etwas nicht richtig initialisiert wurde.
Viele Erklärungen sind bereits zu erklären, wie es geschieht und wie es zu beheben, aber Sie sollten auch folgen Best Practices NullPointerException
überhaupt zu vermeiden.
Siehe auch: eine gute Liste bewährte Praktiken
Ich möchte hinzufügen, sehr wichtig, eine gute Nutzung des final
Modifikator machen.
Mit dem "final" Modifikator, wann immer anwendbar in Java
Zusammenfassung:
- Verwenden Sie den
final
Modifikator gute Initialisierung zu erzwingen. - Vermeiden Sie null in Methoden zurückkehren, zum Beispiel leere Sammlungen zurückkehrt, wenn anwendbar.
- Verwenden Sie Anmerkungen
@NotNull
und@Nullable
- Fehler schnell und Verwendung behauptet Ausbreitung von null Gegenstände durch die gesamte Anwendung zu vermeiden, wenn sie nicht null sein sollte.
- Verwenden Sie gleich mit einem bekannten Objekt zuerst:
if("knownObject".equals(unknownObject)
- Bevorzugen
valueOf()
über toString (). - Verwenden Sie null sichere
StringUtils
MethodenStringUtils.isEmpty(null)
.
Eine Null-Zeiger-Ausnahme ist ein Indikator dafür, dass Sie ein Objekt verwenden, ohne es zu initialisieren.
Zum Beispiel ist eine Student-Klasse, die es in unserem Code verwenden wird.
public class Student {
private int id;
public int getId() {
return this.id;
}
public setId(int newId) {
this.id = newId;
}
}
Der Code unten gibt Ihnen eine Null-Zeiger-Ausnahme.
public class School {
Student student;
public School() {
try {
student.getId();
}
catch(Exception e) {
System.out.println("Null pointer exception");
}
}
}
Weil Sie verwenden student
, aber Sie vergessen haben, es wie in initialisieren die
korrekter Code unten dargestellt:
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");
}
}
}
In Java alles (ohne Urtyp) in Form einer Klasse.
Wenn Sie ein Objekt verwenden möchten, dann haben Sie zwei Phasen:
- Erklären
- Initialisierung
Beispiel:
- Erklärung:
Object object;
- Initialisierung:
object = new Object();
Das Gleiche gilt für das Array-Konzept:
- Erklärung:
Item item[] = new Item[5];
- Initialisierung:
item[0] = new Item();
Wenn Sie nicht den Initialisierungsabschnitt geben dann die NullPointerException
entstehen.
In Java alle Variablen Sie deklarieren sind eigentlich „Referenzen“ auf die Objekte (oder Primitiven) und nicht die Objekte selbst.
Wenn Sie versuchen, ein Objekt Methode auszuführen, fragt die Referenz das lebende Objekt, das Verfahren auszuführen. Aber wenn die Referenz verweist auf NULL (nichts, null, nichtig, nada), dann gibt es keine Möglichkeit, die Methode ausgeführt wird. Dann wird die Laufzeit lassen Sie uns das wissen durch eine Nullpointer werfen.
Ihre Referenz "zeigen" zu null, also "Null -> Pointer".
Das Objekt wohnt in der VM Speicherplatz und die einzige Möglichkeit für den Zugriff ist this
Referenzen verwenden. Nehmen Sie dieses Beispiel:
public class Some {
private int id;
public int getId(){
return this.id;
}
public setId( int newId ) {
this.id = newId;
}
}
Und an einem anderen Ort in Ihrem Code:
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...
Dies ist eine wichtige Sache zu wissen - wenn es keine weiteren Verweise auf ein Objekt (im Beispiel oben, wenn reference
und beide otherReference
Punkt auf null) sind dann das Objekt „nicht erreichbar“. Es gibt keine Art, wie wir mit ihm arbeiten können, so ist dieses Objekt bereit Müll gesammelt zu werden, und irgendwann wird die VM die von diesem Objekt verwendeten Speicher freizugeben und wird ein weiteres zuordnen.
Ein weiteres Auftreten eines NullPointerException
tritt auf, wenn man ein Objekt Array erklärt, versucht dann sofort zu dereferenzieren Elementen innerhalb davon.
String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Diese besondere NPE kann vermieden werden, wenn der Vergleich der Reihenfolge umgekehrt ist; nämlich verwendet .equals
auf einen garantiert nicht-Null-Objekt.
Alle Elemente innerhalb eines Arrays