Beim Lesen von JDK-Quellcode wird häufig festgestellt, dass der Autor die Parameter überprüft, wenn sie null sind, und dann manuell eine neue NullPointerException () auslöst. Warum machen sie das? Ich denke, es ist nicht nötig, dies zu tun, da es eine neue NullPointerException () auslöst, wenn es eine Methode aufruft. (Hier ist zum Beispiel ein Quellcode von HashMap :)
public V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null)
throw new NullPointerException();
Node<K,V> e; V oldValue;
int hash = hash(key);
if ((e = getNode(hash, key)) != null &&
(oldValue = e.value) != null) {
V v = remappingFunction.apply(key, oldValue);
if (v != null) {
e.value = v;
afterNodeAccess(e);
return v;
}
else
removeNode(hash, key, null, false, true);
}
return null;
}
java
nullpointerexception
LiJiaming
quelle
quelle
ArgumentNullException
in solchen Fällen (und nichtNullReferenceException
) ansprechen - es ist eigentlich eine wirklich gute Frage, warum Sie dasNullPointerException
hier explizit ansprechen würden (anstatt eine andere).IllegalArgumentException
oder nichtNullPointerException
. Die JDK-Konvention ist die letztere.Antworten:
Es gibt eine Reihe von Gründen, von denen einige eng miteinander verbunden sind:
Fail-Fast: Wenn es fehlschlägt, ist es am besten, eher früher als später zu scheitern. Auf diese Weise können Probleme näher an ihrer Quelle erkannt werden, sodass sie leichter identifiziert und behoben werden können. Außerdem wird vermieden, dass CPU-Zyklen für Code verschwendet werden, der zum Scheitern verurteilt ist.
Absicht: Durch das explizite Auslösen der Ausnahme wird den Betreuern klar gemacht, dass der Fehler absichtlich vorliegt und der Autor sich der Konsequenzen bewusst war.
Konsistenz: Wenn der Fehler auf natürliche Weise auftreten darf, tritt er möglicherweise nicht in jedem Szenario auf. Wenn beispielsweise keine Zuordnung gefunden wird, wird
remappingFunction
diese niemals verwendet und die Ausnahme wird nicht ausgelöst. Die vorherige Validierung der Eingabe ermöglicht ein deterministischeres Verhalten und eine klarere Dokumentation .Stabilität: Der Code entwickelt sich im Laufe der Zeit. Code, der auf eine Ausnahme stößt, kann nach einigem Refactoring natürlich aufhören oder dies unter anderen Umständen tun. Wenn Sie es explizit werfen, ist es weniger wahrscheinlich, dass sich das Verhalten versehentlich ändert.
quelle
new NullPointerException(message)
Konstruktor verwenden, um zu klären, was null ist. Gut für Leute, die keinen Zugriff auf Ihren Quellcode haben. Sie machten dies sogar zu einem Einzeiler in JDK 8 mit derObjects.requireNonNull(object, message)
Utility-Methode.Dies dient der Klarheit, Konsistenz und um zu verhindern, dass zusätzliche, unnötige Arbeiten ausgeführt werden.
Überlegen Sie, was passieren würde, wenn oben in der Methode keine Schutzklausel vorhanden wäre. Es würde immer anrufen
hash(key)
undgetNode(hash, key)
selbst wennnull
es für das übergeben worden war,remappingFunction
bevor die NPE geworfen wurde.Schlimmer noch, wenn die
if
Bedingung erfüllt istfalse
, nehmen wir denelse
Zweig, der das überhaupt nicht verwendetremappingFunction
, was bedeutet, dass die Methode nicht immer NPE auslöst, wenn a übergebennull
wird. ob dies der Fall ist, hängt vom Status der Karte ab.Beide Szenarien sind schlecht. Wenn dies
null
kein gültiger Wert fürremappingFunction
die Methode ist, sollte unabhängig vom internen Status des Objekts zum Zeitpunkt des Aufrufs konsistent eine Ausnahme ausgelöst werden, und dies sollte ohne unnötige Arbeit geschehen, die sinnlos ist, da sie nur ausgelöst wird. Schließlich ist es ein gutes Prinzip für sauberen, klaren Code, den Wachmann ganz vorne zu haben, damit jeder, der den Quellcode überprüft, leicht erkennen kann, dass dies der Fall ist.Selbst wenn die Ausnahme derzeit von jedem Codezweig ausgelöst würde, ist es möglich, dass eine zukünftige Überarbeitung des Codes dies ändern würde. Wenn Sie die Prüfung zu Beginn durchführen, wird sichergestellt, dass sie definitiv durchgeführt wird.
quelle
Zusätzlich zu den Gründen, die in der hervorragenden Antwort von @ shmosel aufgeführt sind ...
Leistung: Es kann / gab Leistungsvorteile (bei einigen JVMs), die NPE explizit zu werfen, anstatt sie von der JVM ausführen zu lassen.
Dies hängt von der Strategie ab, die der Java-Interpreter und der JIT-Compiler verfolgen, um die Dereferenzierung von Nullzeigern zu erkennen. Eine Strategie besteht darin, nicht auf Null zu testen, sondern das SIGSEGV abzufangen, das auftritt, wenn ein Befehl versucht, auf die Adresse 0 zuzugreifen. Dies ist der schnellste Ansatz, wenn die Referenz immer gültig ist, im NPE-Fall jedoch teuer .
Ein expliziter Test
null
im Code würde den SIGSEGV-Leistungseinbruch in einem Szenario vermeiden, in dem NPEs häufig waren.(Ich bezweifle, dass dies eine lohnende Mikrooptimierung in einer modernen JVM wäre, aber es könnte in der Vergangenheit gewesen sein.)
Kompatibilität: Der wahrscheinliche Grund dafür, dass die Ausnahme keine Meldung enthält, ist die Kompatibilität mit NPEs, die von der JVM selbst ausgelöst werden. In einer kompatiblen Java-Implementierung hat eine von der JVM ausgelöste NPE eine
null
Nachricht. (Android Java ist anders.)quelle
Abgesehen von dem, worauf andere hingewiesen haben, ist es wert, die Rolle der Konvention hier zu erwähnen. In C # haben Sie beispielsweise auch die gleiche Konvention, in solchen Fällen explizit eine Ausnahme auszulösen, aber es ist speziell eine
ArgumentNullException
, die etwas spezifischer ist. (Die C # -Konvention besagt, dass es sichNullReferenceException
immer um einen Fehler handelt - ganz einfach, es sollte niemals im Produktionscode auftreten; dies istArgumentNullException
normalerweise auch der Fall, aber es könnte sich eher um einen Fehler im Sinne von "Sie tun es nicht" handeln verstehen, wie man die Bibliothek richtig benutzt "Art von Fehler).Im Grunde
NullReferenceException
bedeutet C # , dass Ihr Programm tatsächlich versucht hat, es zu verwenden, währendArgumentNullException
es bedeutet, dass es erkannt hat, dass der Wert falsch war, und dass es sich nicht einmal die Mühe gemacht hat, ihn zu verwenden. Die Auswirkungen können tatsächlich unterschiedlich sein (abhängig von den Umständen), daArgumentNullException
die betreffende Methode noch keine Nebenwirkungen hatte (da die Methodenvoraussetzungen nicht erfüllt wurden).Übrigens, wenn Sie so etwas wie
ArgumentNullException
oderIllegalArgumentException
ansprechen, ist dies ein Teil des Punktes, an dem Sie die Prüfung durchführen: Sie möchten eine andere Ausnahme als "normalerweise".In beiden Fällen wird durch das explizite Auslösen der Ausnahme die bewährte Methode verstärkt, die Voraussetzungen und erwarteten Argumente Ihrer Methode explizit anzugeben, wodurch der Code leichter zu lesen, zu verwenden und zu warten ist. Wenn Sie nicht explizit nachgesehen haben
null
, weiß ich nicht, ob es daran liegt, dass Sie dachten, dass niemand jemals einnull
Argument übergeben würde, Sie zählen es, um die Ausnahme trotzdem auszulösen, oder Sie haben einfach vergessen, danach zu suchen.quelle
null
Objekts hinzuweisen ." Ich bin nicht verrückt danach, aber das scheint das beabsichtigte Design zu sein.NullReferenceException
(äquivalent zuNullPointerException
) bedeutet, dass Ihr Code tatsächlich versucht hat, ihn zu verwenden (was immer ein Fehler ist - es sollte niemals im Produktionscode vorkommen), vs. "Ich weiß, dass das Argument falsch ist, also habe ich es nicht einmal getan." versuche es zu benutzen. " Es gibt auchArgumentException
(was bedeutet, dass das Argument aus einem anderen Grund falsch war).Es ist so, dass Sie die Ausnahme erhalten, sobald Sie den Fehler begangen haben, und nicht später, wenn Sie die Karte verwenden und nicht verstehen, warum es passiert ist.
quelle
Es verwandelt eine scheinbar unberechenbare Fehlerbedingung in eine eindeutige Vertragsverletzung: Die Funktion hat einige Voraussetzungen für ein korrektes Funktionieren, sodass sie im Voraus überprüft und die Einhaltung erzwungen wird.
Der Effekt ist, dass Sie nicht debuggen müssen,
computeIfPresent()
wenn Sie die Ausnahme daraus ziehen. Sobald Sie sehen, dass die Ausnahme von der Vorbedingungsprüfung stammt, wissen Sie, dass Sie die Funktion mit einem unzulässigen Argument aufgerufen haben. Wenn die Prüfung nicht vorhanden wäre, müssten Sie die Möglichkeit ausschließen, dass ein Fehler in sichcomputeIfPresent()
selbst vorliegt , der dazu führt, dass die Ausnahme ausgelöst wird.Offensichtlich ist das Werfen des Generikums
NullPointerException
eine wirklich schlechte Wahl, da es an und für sich keine Vertragsverletzung signalisiert.IllegalArgumentException
wäre eine bessere Wahl.Nebenbemerkung:
Ich weiß nicht, ob Java dies zulässt (ich bezweifle es), aber C / C ++ - Programmierer verwenden
assert()
in diesem Fall eine, die für das Debuggen wesentlich besser ist: Sie weist das Programm an, sofort und so hart wie möglich abzustürzen, falls dies bereitgestellt wird Bedingung zu falsch bewerten. Also, wenn du gelaufen bistUnter einem Debugger und etwas, das
NULL
in eines der beiden Argumente übergeht, stoppt das Programm direkt an der Zeile, dieNULL
angibt , um welches Argument es sich handelt , und Sie können alle lokalen Variablen des gesamten Aufrufstapels nach Belieben untersuchen.quelle
assert something != null;
Dies erfordert jedoch das-assertions
Flag, wenn die App ausgeführt wird. Wenn das-assertions
Flag nicht vorhanden ist,Es ist, weil es möglich ist, dass es nicht natürlich passiert. Sehen wir uns einen Code wie diesen an:
(Natürlich gibt es in diesem Beispiel zahlreiche Probleme mit der Codequalität. Tut mir leid, dass ich mir ein perfektes Beispiel vorstellen kann.)
Situation, in
c
der nicht tatsächlich verwendet wird undNullPointerException
nicht geworfen wird, auch wenn diesc == null
möglich ist.In komplizierteren Situationen wird es sehr einfach, solche Fälle aufzuspüren. Aus diesem Grund ist eine allgemeine Überprüfung
if (c == null) throw new NullPointerException()
besser.quelle
Es ist beabsichtigt, weiteren Schaden zu schützen oder in einen inkonsistenten Zustand zu gelangen.
quelle
Neben all den anderen hervorragenden Antworten hier möchte ich auch einige Fälle hinzufügen.
Sie können eine Nachricht hinzufügen, wenn Sie eine eigene Ausnahme erstellen
Wenn Sie Ihre eigenen werfen
NullPointerException
, können Sie eine Nachricht hinzufügen (was Sie auf jeden Fall sollten!)Die Standardnachricht ist beispielsweise eine
null
vonnew NullPointerException()
und alle Methoden, die sie verwendenObjects.requireNonNull
. Wenn Sie diese Null drucken, kann sie sogar in eine leere Zeichenfolge übersetzt werden ...Ein bisschen kurz und nicht informativ ...
Der Stack-Trace gibt viele Informationen, aber damit der Benutzer weiß, was null war, muss er den Code ausgraben und die genaue Zeile anzeigen.
Stellen Sie sich nun vor, dass NPE verpackt und über das Internet gesendet wird, z. B. als Nachricht in einem Webdienstfehler, möglicherweise zwischen verschiedenen Abteilungen oder sogar Organisationen. Im schlimmsten Fall kann niemand herausfinden, wofür
null
...Verkettete Methodenaufrufe halten Sie auf dem Laufenden
Eine Ausnahme sagt Ihnen nur, in welcher Zeile die Ausnahme aufgetreten ist. Betrachten Sie die folgende Zeile:
Wenn Sie eine NPE erhalten und diese auf diese Zeile zeigt, welche von
repository
undsomeObject
war null?Stattdessen zeigt das Überprüfen dieser Variablen, wenn Sie sie erhalten, zumindest auf eine Zeile, in der sie hoffentlich die einzige Variable sind, die behandelt wird. Und wie bereits erwähnt, noch besser, wenn Ihre Fehlermeldung den Namen der Variablen oder ähnliches enthält.
Fehler bei der Verarbeitung vieler Eingaben sollten identifizierende Informationen liefern
Stellen Sie sich vor, Ihr Programm verarbeitet eine Eingabedatei mit Tausenden von Zeilen und plötzlich gibt es eine NullPointerException. Sie sehen sich den Ort an und stellen fest, dass einige Eingaben falsch waren ... welche Eingaben? Sie benötigen weitere Informationen zur Zeilennummer, möglicherweise zur Spalte oder sogar zum gesamten Zeilentext, um zu verstehen, welche Zeile in dieser Datei korrigiert werden muss.
quelle