Qu'est-ce qu'un NullPointerException, et comment puis-je le corriger?
que sont les exceptions Null Pointer ( java.lang.NullPointerException
) et quelles en sont les causes?
quelles méthodes / outils peuvent être utilisés pour déterminer la cause afin que vous arrêtiez l'exception de causer le programme de mettre fin prématurément?
12 réponses
quand vous déclarez une variable de référence (i.e. un objet) vous créez vraiment un pointeur vers un objet. Considérez le code suivant où vous déclarez une variable de type primitif int
:
int x;
x = 10;
dans cet exemple, la variable x est une int
et Java l'initialisera à 0 pour vous. Lorsque vous affectez à 10 dans la deuxième ligne de votre valeur 10 est écrit dans l'emplacement mémoire pointé par x.
mais, quand vous essayez de déclarer un type de référence quelque chose de différent se produit. Prenez le code suivant:
Integer num;
num = new Integer(10);
la première ligne déclare une variable nommée num
, mais elle ne contient pas de valeur primitive. Au lieu de cela, il contient un pointeur (parce que le type est Integer
qui est un type de référence). Comme vous n'avez pas encore dit ce qu'il faut pointer vers Java le met à nul, ce qui signifie Je ne pointe à rien ".
dans le deuxième ligne, le mot-clé new
est utilisé pour instancier (ou créer) un objet de type entier et la variable pointeur num
est assignée à cet objet. Vous pouvez maintenant référencer l'objet en utilisant l'opérateur dereferencing .
(un point).
le Exception
que vous avez demandé se produit lorsque vous déclarez une variable mais n'a pas créé un objet. Si vous essayez de dereference num
avant de créer l'objet vous obtenez un NullPointerException
. Dans la plupart des cas triviaux, le compilateur va attraper le problème et vous faire savoir que "num peut ne pas avoir été initialisé", mais parfois vous écrivez du code qui ne crée pas directement l'objet.
Par exemple, vous pouvez avoir une méthode comme suit:
public void doSomething(SomeObject obj) {
//do something to obj
}
dans ce cas , vous ne créez pas l'objet obj
, en présumant plutôt qu'il a été créé avant que la méthode doSomething
soit appelée. Malheureusement, il est possible d'appeler la méthode comme ceci:
doSomething(null);
, auquel cas obj
est nul. Si la méthode est destinée à faire quelque chose à l'objet passé-dans, Il est approprié de jeter le NullPointerException
parce que c'est une erreur de programmeur et le programmeur aura besoin de cette information à des fins de débogage.
il peut aussi y avoir des cas où le but de la méthode n'est pas seulement de fonctionner sur l'objet transmis, et par conséquent un paramètre nul peut être acceptable. Dans ce cas, vous devez vérifier pour un paramètre null et se comporter différemment. Vous devez également expliquer dans la documentation. Par exemple, doSomething
pourrait s'écrire comme:
/**
* @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
}
}
enfin, comment identifier l'exception et la cause en utilisant la trace de la pile
NullPointerException
s sont des exceptions qui se produisent lorsque vous essayez d'utiliser une référence qui pointe à aucun endroit dans la mémoire (null) comme s'il référençait un objet. Appeler une méthode sur une référence nulle ou essayer d'accéder à un champ d'une référence nulle déclenchera un NullPointerException
. Ce sont les plus communs, mais d'autres moyens sont énumérés sur la NullPointerException
javadoc page.
probablement le code d'exemple le plus rapide que j'ai pu trouver pour illustrer un NullPointerException
serait:
public class Example {
public static void main(String[] args) {
Object obj = null;
obj.hashCode();
}
}
sur la première ligne à l'intérieur de main
, je mets explicitement Object
référence obj
égale à null
. Cela signifie que j'ai une référence, mais elle ne montre aucun objet. Après cela, j'essaie de traiter la référence comme si elle pointe vers un objet en appelant une méthode sur elle. Il en résulte un NullPointerException
parce qu'il n'y a pas de code à exécuter dans l'emplacement que la référence pointe.
(c'est un détail technique, mais je pense qu'il vaut la peine de le mentionner: une référence qui pointe vers nul n'est pas la même chose qu'un pointeur C qui pointe vers un emplacement mémoire invalide. Un pointeur nul ne pointe littéralement pas n'importe où , ce qui est subtilement différent de pointer vers un endroit qui se trouve être invalide.)
Qu'est-ce qu'une NullPointerException?
Un bon endroit pour commencer est le Javadoc . Ils ont ceci couvert:
lancé lorsqu'une application tente d'utiliser null dans un cas où un l'objet est nécessaire. Il s'agit notamment de:
- appelant la méthode d'instance d'un objet nul.
- accès ou modification du champ d'un objet nul.
- prenant la longueur de null comme s'il s'agissait d'un tableau.
- accéder ou modifier les fentes de null comme si c'était un tableau.
- lancer null comme si c'était une valeur Lancable.
les demandes doivent mentionner les instances de cette classe pour indiquer les autres l'usage illicite de l'objet nul.
il est également le cas que si vous tentez d'utiliser un null référence avec synchronized
, qui lancera également cette exception, selon le JLS :
SynchronizedStatement: synchronized ( Expression ) Block
- Sinon, si la valeur de L'Expression est nulle, un
NullPointerException
est lancé.
Comment puis-je le réparer?
donc vous avez un NullPointerException
. Comment voulez-vous résoudre ce problème? Prenons un exemple simple qui jette un 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();
}
}
identifiez les valeurs nulles
la première étape consiste à identifier exactement les valeurs qui causent l'exception . Pour cela, nous avons besoin de faire un peu de débogage. Il est important d'apprendre à lire un stacktrace . Cela vous montrera où l'exception a été jetée:
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)
ici, nous voyons que l'exception est jetée sur la ligne 13 (dans le printString
). Regardez la ligne et vérifiez quelles valeurs sont nulles par
l'ajout de l'enregistrement des déclarations ou à l'aide d'un débogueur . Nous découvrons que s
est nul, et appeler la méthode length
sur elle jette l'exception. Nous pouvons voir que le programme cesse de lancer l'exception lorsque s.length()
est supprimé de la méthode.
Trace où ces valeurs proviennent de
vérifiez ensuite d'où vient cette valeur. En suivant les appelants de la méthode, nous voyons que s
est transmis avec printString(name)
dans le print()
, et this.name
est null.
Trace où ces valeurs doivent être réglées
Où est fixé this.name
? Dans la méthode setName(String)
. Avec un peu plus de débogage, nous pouvons voir que cette méthode n'est pas appelée à tous. Si la méthode a été appelée, assurez-vous de vérifier le ordre que ces méthodes sont appelées, et la méthode d'ensemble n'est pas appelé après la méthode d'impression.
cela suffit pour nous donner une solution: ajouter un appel à printer.setName()
avant d'appeler printer.print()
.
autres dispositifs fixes
la variable peut avoir une valeur par défaut (et setName
peut empêcher qu'elle soit définie à null):
private String name = "";
soit la méthode print
soit la méthode printString
peut cochez la case "null , par exemple:
printString((name == null) ? "" : name);
ou vous pouvez concevoir la classe de sorte que name
a toujours une valeur non nulle :
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();
}
}
Voir aussi:
Je ne trouve toujours pas le problème
Si vous avez essayé de déboguer le problème et n'ont toujours pas de solution, vous pouvez poster une question pour plus d'aide, mais assurez-vous d'inclure ce que vous avez essayé jusqu'à présent. À tout le moins, inclut la chaîne de caractères dans la question, et marque les numéros de ligne importants dans le code. Aussi, essayer de simplifier le code d'abord (voir SSCCE ).
Question: Qu'est-ce qui cause un NullPointerException
(NPE)?
comme vous devez le savoir, les types Java sont divisés en types primitifs ( boolean
, int
, etc.) et types de référence . Les types de référence en Java vous permettent d'utiliser la valeur spéciale null
qui est la façon Java de dire "aucun objet".
A NullPointerException
est lancé à l'exécution chaque fois que votre programme tente d'utiliser un null
comme si c'était une vraie référence. Par exemple, si vous écrivez ceci:
public class Test {
public static void main(String[] args) {
String foo = null;
int length = foo.length(); // HERE
}
}
l'énoncé étiqueté" ici "va tenter d'exécuter la méthode length()
sur une référence null
, et cela lancera un NullPointerException
.
il y a plusieurs façons d'utiliser une valeur null
qui donnera un résultat NullPointerException
. En fait, les seules choses que vous peut faire avec un null
sans la cause d'une NPE sont:
- l'affecter à une variable de référence ou de lire à partir d'une variable de référence,
- l'attribuer à un élément de tableau ou de lire à partir d'un élément de tableau (à condition que la matrice de référence lui-même est non-nulle!),
- passer en tant que paramètre ou le retourner comme un résultat, ou
- tester en utilisant les opérateurs
==
ou!=
, ouinstanceof
.
Question: Comment lire la NPE stacktrace?
supposons que je compile et exécute le programme ci-dessus:
$ javac Test.java
$ java Test
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:4)
$
première observation: la compilation réussit! Le problème dans le programme n'est pas une erreur de compilation. C'est une erreur runtime . (Certains IDEs peuvent prévenir votre programme de toujours jeter une exception ... mais pas le compilateur standard javac
.)
deuxième observation: quand je lance le programme, il affiche deux lignes de "charabia". faux!! ce n'est pas charabia. C'est un stacktrace ... et il fournit informations vitales qui vous aideront à traquer l'erreur dans votre code, si vous prenez le temps de lire attentivement.
Alors, regardons ce qu'il dit:
Exception in thread "main" java.lang.NullPointerException
La première ligne de la trace de la pile vous dit un certain nombre de choses:
- il vous indique le nom du fil Java dans lequel l'exception a été jetée. Pour un programme simple avec un fil (comme celui-ci), il sera "principal". Passons sur ...
- il vous indique le nom complet de l'exception qui a été lancée; i.e.
java.lang.NullPointerException
. - si l'exception a un message d'erreur associé, qui sera affiché après le nom de l'exception.
NullPointerException
est inhabituel à cet égard, parce qu'il a rarement une message d'erreur.
la deuxième ligne est la plus importante pour diagnostiquer une NPE.
at Test.main(Test.java:4)
cela nous dit un certain nombre de choses:
- "au Test.main "dit que nous étions dans la méthode
main
de la classeTest
. - " Test.java: 4" donne le nom du fichier source de la classe, et il nous dit que la déclaration où cela s'est produit est dans la ligne 4 du fichier.
si vous comptez les lignes dans le fichier ci-dessus, la ligne 4 est celle que j'ai étiqueté avec le commentaire "ici".
notez que dans un exemple plus compliqué, il y aura beaucoup de lignes dans la trace de la pile NPE. Mais vous pouvez être sûr que la deuxième ligne (la première ligne "at") vous dira où le NPE a été lancé 1 .
en bref, la trace de la pile nous indiquera sans ambiguïté quelle déclaration de le programme a lancé le NPE.
1-pas tout à fait vrai. Il y a des choses qu'on appelle des exceptions imbriquées...
Question: Comment retrouver la cause de l'exception NPE dans mon code?
C'est la partie difficile. La réponse courte est d'appliquer l'inférence logique aux preuves fournies par la trace de la pile, le code source et la documentation pertinente de L'API.
Let's illustrer avec l'exemple simple (ci-dessus). Nous commençons par regarder la ligne que la trace de la pile nous a indiqué est où le NPE s'est produit:
int length = foo.length(); // HERE
comment ça peut lancer un NPE?
en fait, il n'y a qu'un seul moyen: il ne peut se produire que si foo
a la valeur null
. Nous essayons ensuite d'exécuter la méthode length()
sur null
et .... BANG!
mais (je vous entends dire) que faire si le NPE a été jeté dans l'appel de méthode length()
?
si c'était le cas, la trace de la pile serait différente. La première ligne" at "dirait que l'exception a été jetée dans une certaine ligne dans la classe java.lang.String
, et la ligne 4 de Test.java
serait la deuxième ligne" at".
alors d'où vient ce null
? Dans ce cas, il est évident, et il est évident que nous devons faire pour le réparer. (Attribuez une valeur non nulle à foo
.)
OK, donc essayons un exemple un peu plus délicat. Cela nécessitera une déduction logique de .
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)
$
donc maintenant nous avons deux lignes" at". La première est pour cette ligne:
return args[pos].length();
et la seconde est pour cette ligne:
int length = test(foo, 1);
en regardant la première ligne, comment cela pourrait-il lancer un NPE? Il y a deux façons:
- si la valeur de
bar
estnull
puisbar[pos]
lancera un NPE. - si la valeur de
bar[pos]
estnull
alors appelerlength()
sur elle lancera un NPE.
ensuite, nous devons déterminer lequel de ces scénarios explique ce qui se passe réellement. Nous commencerons par explorer la première:
d'Où vient bar
? C'est un paramètre de la méthode test
appel, et si nous regardons comment test
a été appelé, nous pouvons voir qu'il provient de la variable statique foo
. En outre, nous pouvons voir clairement que nous avons initialisé foo
à une valeur non nulle. Cela suffit pour rejeter provisoirement cette explication. (En théorie, quelque chose d'autre pourrait changement foo
à null
... mais ce n'est pas ce qui se passe ici.)
alors qu'en est-il de notre deuxième scénario? Eh bien, nous pouvons voir que pos
est 1
, ce qui signifie que foo[1]
doit être null
. Est-ce possible?
en effet, il est! Et c'est le problème. Quand nous initialisons comme ceci:
private static String[] foo = new String[2];
nous attribuons un String[]
avec deux éléments qui sont initialisés à null
. Après cela, nous n'avons pas changé le contenu de foo
... donc foo[1]
sera toujours null
.
C'est comme si vous essayez d'accéder à un objet qui est null
. Prenons l'exemple suivant:
TypeA objA;
en ce moment vous avez juste déclaré cet objet mais pas initialisé ou instancié . Et chaque fois que vous essayez d'accéder à une propriété ou une méthode en elle, il lancera NullPointerException
qui a du sens.
voir l'exemple ci-dessous:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
une exception de pointeur nul est lancée lorsqu'une application tente d'utiliser null dans un cas où un objet est requis. Il s'agit notamment de:
- appelant la méthode d'instance d'un objet
null
. - accéder ou modifier le champ d'un objet
null
. - prenant la longueur de
null
comme s'il s'agissait d'un tableau. - accéder ou modifier les slots de
null
comme si ont été d'un tableau. - lancer
null
comme si c'était une valeur Lancable.
les applications doivent lancer des instances de cette classe pour indiquer d'autres utilisations illégales de l'objet null
.
référence: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
a NULL
pointeur est un qui pointe à nulle part. Lorsque vous déréférencement d'un pointeur p
, dites-vous "donnez-moi les données à l'emplacement stocké en "p". Quand p
est un pointeur nul, l'emplacement stocké dans p
est nowhere
, vous dites"donnez-moi les données à l'emplacement "nulle part". Évidemment, il ne peut pas faire ça, donc il lance un NULL pointer exception
.
En général, c'est parce que quelque chose n'a pas été initialisé correctement.
de nombreuses explications sont déjà présentes pour expliquer comment cela se produit et comment le corriger, mais vous devez également suivre meilleures pratiques pour éviter NullPointerException
du tout.
voir aussi: Une bonne liste de meilleures pratiques
j'ajouterais, très important, faire un bon usage du modificateur final
.
utilisant le modificateur" final "chaque fois qu'applicable en Java
résumé:
- utilisez le modificateur
final
pour appliquer une bonne initialisation. - éviter de renvoyer null dans les méthodes, par exemple en retournant les collections vides le cas échéant.
- utiliser les annotations
@NotNull
et@Nullable
- Fail fast et l'utilisation affirme pour éviter la propagation des objets nuls par le biais de l'ensemble de l'application quand ils ne devraient pas être NULL.
- utiliser égal avec un objet connu en premier:
if("knownObject".equals(unknownObject)
- Préfèrent
valueOf()
sur toString(). - Utiliser la valeur null sûr
StringUtils
méthodesStringUtils.isEmpty(null)
.
Une exception de pointeur null est un indicateur que vous utilisez un objet sans l'initialiser.
par exemple, ci-dessous est une classe d'étudiant qui va l'utiliser dans notre code.
public class Student {
private int id;
public int getId() {
return this.id;
}
public setId(int newId) {
this.id = newId;
}
}
le code ci-dessous vous donne une exception de pointeur nul.
public class School {
Student obj_Student;
public School() {
try {
obj_Student.getId();
}
catch(Exception e) {
System.out.println("Null Pointer ");
}
}
}
parce que vous utilisez Obj_Student
, mais vous avez oublié de l'initialiser comme dans le
code correct indiqué ci-dessous:
public class School {
Student obj_Student;
public School() {
try {
obj_Student = new Student();
obj_Student.setId(12);
obj_Student.getId();
}
catch(Exception e) {
System.out.println("Null Pointer ");
}
}
}
en Java, tout est sous forme de classe.
si vous voulez utiliser n'importe quel objet alors vous avez deux phases:
- Déclarer
- initialisation
exemple:
- déclaration:
Object a;
- initialisation:
a=new Object();
de Même pour le tableau de concept
- déclaration:
Item i[]=new Item[5];
- initialisation:
i[0]=new Item();
si vous ne donnez pas la section d'initialisation, alors le NullpointerException
apparaît.
In Java toutes les variables que vous déclarez sont en fait des" références " aux objets (ou primitives) et non aux objets eux-mêmes.
lorsque vous tentez d'exécuter une méthode objet, la référence demande à l'objet vivant d'exécuter cette méthode. Mais si la référence renvoie à NULL (nothing, zero, void, nada) alors il n'y a aucun moyen que la méthode soit exécutée. Alors le runtime vous le fait savoir en lançant une NullPointerException.
votre référence est" pointer "vers null, donc"Null -> pointeur".
l'objet vit dans l'espace mémoire VM et la seule façon d'y accéder est d'utiliser les références this
. Prenez cet exemple:
public class Some {
private int id;
public int getId(){
return this.id;
}
public setId( int newId ) {
this.id = newId;
}
}
et à un autre endroit dans votre 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...
c'est une chose importante à savoir - quand il n'y a plus de références à un objet (dans l'exemple ci-dessus quand reference
et otherReference
les deux pointent vers null) alors l'objet est"injoignable". Il n'y a aucun moyen que nous puissions travailler avec elle, donc cet objet est prêt à être collecté, et à un moment donné, la VM libérera la mémoire utilisée par cet objet et en affectera une autre.
une autre occurrence d'un NullPointerException
se produit lorsqu'on déclare un tableau d'objets, puis qu'on essaie immédiatement de déréférencer des éléments à l'intérieur de celui-ci.
String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
cette NPE particulière peut être évitée si l'ordre de comparaison est inversé; à savoir, utiliser .equals
sur un objet non nul garanti.
Tous les éléments à l'intérieur d'un tableau sont initialisés à leur valeur initiale ; pour toute type d'objet tableau, cela signifie que tous les éléments sont 151940920" .
Vous doit initialiser les éléments dans le tableau avant l'accès ou de déréférencement.
String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}