Réponses:
Lorsque vous déclarez une variable de référence (c'est-à-dire un objet), vous créez vraiment un pointeur sur 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 un int
et Java l'initialisera 0
pour vous. Lorsque vous lui affectez la valeur de 10
sur la deuxième ligne, votre valeur de 10
est écrite dans l'emplacement mémoire désigné par x
.
Mais, lorsque 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 encore de valeur primitive. Au lieu de cela, il contient un pointeur (car le type est Integer
qui est un type de référence). Puisque vous n'avez pas encore dit sur quoi pointer, Java le définit null
, ce qui signifie « Je ne pointe vers rien ».
Dans la deuxième ligne, le new
mot-clé est utilisé pour instancier (ou créer) un objet de type Integer
et la variable pointeur num
est affectée à cet Integer
objet.
Cela NullPointerException
se produit lorsque vous déclarez une variable mais que vous n'avez pas créé d'objet et que vous l'assignez à la variable avant d'essayer d'utiliser le contenu de la variable (appelé déréférencement ). Vous pointez donc quelque chose qui n'existe pas réellement.
Le déréférencement se produit généralement lors de l'utilisation .
pour accéder à une méthode ou un champ, ou [
pour indexer un tableau.
Si vous essayez de déréférencer num
AVANT de créer l'objet, vous obtenez un NullPointerException
. Dans les cas les plus triviaux, le compilateur détectera le problème et vous fera savoir que " num may not have been initialized
," mais parfois vous pouvez écrire 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
, mais supposez plutôt qu'il a été créé avant l' doSomething()
appel de la méthode. Remarque, il est possible d'appeler la méthode comme ceci:
doSomething(null);
Dans ce cas, obj
est null
. Si la méthode est destinée à faire quelque chose à l'objet transmis, il convient de lancer le NullPointerException
car c'est une erreur de programmeur et le programmeur aura besoin de ces informations à des fins de débogage. Veuillez inclure le nom de la variable objet dans le message d'exception, comme
Objects.requireNonNull(a, "a");
Alternativement, il peut y avoir des cas où le but de la méthode n'est pas uniquement d'opérer sur l'objet transmis, et donc un paramètre nul peut être acceptable. Dans ce cas, vous devrez vérifier un paramètre nul et vous comporter différemment. Vous devez également expliquer cela dans la documentation. Par exemple, doSomething()
pourrait s'écrire:
/**
* @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 localiser l'exception et la cause en utilisant Stack Trace
Quelles méthodes / outils peuvent être utilisés pour déterminer la cause afin que vous empêchiez l'exception de provoquer l'arrêt prématuré du programme?
Sonar avec findbugs peut détecter NPE. Le sonar peut-il intercepter dynamiquement les exceptions de pointeur nul causées par JVM
int a=b
peut lancer un NPE si b est un Integer
. Il y a des cas où cela prête à confusion pour déboguer.
NullPointerException
problèmes dans votre code est d'utiliser @Nullable
et d' @NotNull
annoter. La réponse suivante contient plus d'informations à ce sujet. Bien que cette réponse concerne spécifiquement l'IDE IntelliJ, elle est également applicable à d'autres outils comme l'est apparanet des commentaires. (BTW je ne suis pas autorisé à modifier cette réponse directement, peut-être que l'auteur peut l'ajouter?)
NullPointerException
s sont des exceptions qui se produisent lorsque vous essayez d'utiliser une référence qui ne pointe vers aucun emplacement en mémoire (null) comme si elle faisait référence à 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 a NullPointerException
. Ce sont les plus courants, mais d'autres moyens sont répertoriés sur la NullPointerException
page javadoc.
L'exemple de code le plus rapide que je pourrais illustrer NullPointerException
serait probablement:
public class Example {
public static void main(String[] args) {
Object obj = null;
obj.hashCode();
}
}
Sur la première ligne à l'intérieur main
, je définis explicitement la Object
référence obj
égale à null
. Cela signifie que j'ai une référence, mais qu'elle ne pointe vers 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 dessus. Il en résulte un NullPointerException
car il n'y a pas de code à exécuter à l'emplacement vers lequel pointe la référence.
(Ceci est une technicité, mais je pense qu'il convient de mentionner: Une référence qui pointe vers null n'est pas la même chose qu'un pointeur C qui pointe vers un emplacement de mémoire non valide. Un pointeur nul ne pointe littéralement nulle part , ce qui est subtilement différent de pointant vers un emplacement qui n'est pas valide.)
null
qu'avant de l'utiliser, comme ceci . Avec les variables locales, le compilateur intercepterait cette erreur, mais dans ce cas, ce n'est pas le cas. Peut-être que cela apporterait un complément utile à votre réponse?
Un bon point de départ est les JavaDocs . Ils ont ceci couvert:
Lancé lorsqu'une application tente d'utiliser null dans le cas où un objet est requis. Ceux-ci inclus:
- Appel de la méthode d'instance d'un objet nul.
- Accès ou modification du champ d'un objet nul.
- Prendre la longueur de null comme s'il s'agissait d'un tableau.
- Accéder ou modifier les emplacements de null comme s'il s'agissait d'un tableau.
- Lancer null comme s'il s'agissait d'une valeur Throwable.
Les applications doivent lancer des instances de cette classe pour indiquer d'autres utilisations illégales de l'objet null.
Il est également possible que si vous essayez d'utiliser une référence nulle avec synchronized
, cela lèvera également cette exception, par le JLS :
SynchronizedStatement: synchronized ( Expression ) Block
- Sinon, si la valeur de l'expression est nulle, a
NullPointerException
est levé.
Vous avez donc un NullPointerException
. Comment le corrigez-vous? Prenons un exemple simple qui lance 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();
}
}
Identifier les valeurs nulles
La première étape consiste à identifier exactement les valeurs qui provoquent l'exception . Pour cela, nous devons effectuer un débogage. Il est important d'apprendre à lire une trace de pile . Cela vous montrera où l'exception a été levé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 levée sur la ligne 13 (dans la printString
méthode). Regardez la ligne et vérifiez quelles valeurs sont nulles en ajoutant des instructions de journalisation ou en utilisant un débogueur . Nous découvrons que s
c'est nul, et appeler la length
méthode dessus lève l'exception. Nous pouvons voir que le programme cesse de lever l'exception lorsqu'il s.length()
est supprimé de la méthode.
Tracer l'origine de ces valeurs
Vérifiez ensuite d'où vient cette valeur. En suivant les appelants de la méthode, nous voyons que cela s
est passé avec printString(name)
dans la print()
méthode et this.name
est nul.
Trace où ces valeurs doivent être définies
Où est this.name
placé? Dans la setName(String)
méthode. Avec un peu plus de débogage, nous pouvons voir que cette méthode n'est pas appelée du tout. Si la méthode a été appelée, vérifiez l' ordre dans lequel ces méthodes sont appelées et la méthode set n'est pas appelée après la méthode d'impression.
Cela suffit pour nous donner une solution: ajoutez un appel à printer.setName()
avant d'appeler printer.print()
.
La variable peut avoir une valeur par défaut (et setName
peut empêcher qu'elle soit définie sur null):
private String name = "";
La méthode print
ou printString
peut vérifier la valeur null , par exemple:
printString((name == null) ? "" : name);
Ou vous pouvez concevoir la classe de sorte qu'elle name
ait 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 également:
Si vous avez essayé de déboguer le problème et que vous n'avez toujours pas de solution, vous pouvez publier une question pour obtenir de l'aide, mais assurez-vous d'inclure ce que vous avez essayé jusqu'à présent. Au minimum, incluez le stacktrace dans la question et marquez les numéros de ligne importants dans le code. Essayez également de simplifier le code en premier (voir SSCCE ).
NullPointerException
(NPE)?Comme vous devez le savoir, les types Java sont divisés en types primitifs ( boolean
, int
, etc.) et les 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 "pas d'objet".
A NullPointerException
est lancé lors de l'exécution à chaque fois que votre programme tente d'utiliser un null
comme s'il s'agissait d'une véritable 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'instruction intitulée "ICI" va tenter d'exécuter la length()
méthode sur une null
référence, ce qui lancera a NullPointerException
.
Il existe de nombreuses façons d'utiliser une null
valeur qui entraînera un NullPointerException
. En fait, les seules choses que vous pouvez faire avec un null
sans provoquer un NPE sont:
==
ou !=
opérateurs, ou instanceof
.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)
$
Premier constat: la compilation réussit! Le problème dans le programme n'est PAS une erreur de compilation. Il s'agit d'une erreur d' exécution . (Certains IDE peuvent avertir que votre programme lèvera toujours une exception ... mais pas le javac
compilateur standard .)
Deuxième constat: lorsque je lance le programme, il sort deux lignes de "gobbledy-gook". FAUX!! Ce n'est pas gobbledy-gook. Il s'agit d'une trace de pile ... et elle fournit des informations vitales qui vous aideront à retrouver l'erreur dans votre code si vous prenez le temps de la lire attentivement.
Voyons donc ce qu'il dit:
Exception in thread "main" java.lang.NullPointerException
La première ligne de la trace de pile vous indique un certain nombre de choses:
java.lang.NullPointerException
.NullPointerException
est inhabituel à cet égard, car il contient rarement un message d'erreur.La deuxième ligne est la plus importante pour diagnostiquer un NPE.
at Test.main(Test.java:4)
Cela nous indique un certain nombre de choses:
main
méthode de la Test
classe.Si vous comptez les lignes dans le fichier ci-dessus, la ligne 4 est celle que j'ai étiquetée avec le commentaire "ICI".
Notez que dans un exemple plus compliqué, il y aura beaucoup de lignes dans la trace de 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 dira sans ambiguïté quelle instruction du programme a jeté le NPE.
1 - Pas tout à fait vrai. Il y a des choses appelées exceptions imbriquées ...
C'est la partie difficile. La réponse courte consiste à appliquer une inférence logique aux preuves fournies par la trace de pile, le code source et la documentation API appropriée.
Illustrons d'abord l'exemple simple (ci-dessus). Nous commençons par regarder la ligne que la trace de pile nous a indiqué est l'endroit où le NPE s'est produit:
int length = foo.length(); // HERE
Comment cela peut-il lancer un NPE?
En fait, il n'y a qu'une seule façon: cela ne peut arriver que si foo
a la valeur null
. Nous essayons ensuite d'exécuter la length()
méthode null
et ... BANG!
Mais (je vous entends dire) et si le NPE était jeté à l'intérieur de l' length()
appel de méthode?
Eh bien, si cela se produisait, la trace de la pile serait différente. La première ligne "at" dirait que l'exception a été levée dans une ligne de la java.lang.String
classe et la ligne 4 de Test.java
serait la deuxième ligne "at".
D'où cela null
vient-il? Dans ce cas, c'est évident, et il est évident ce que nous devons faire pour y remédier. (Attribuez une valeur non nulle à foo
.)
OK, essayons donc un exemple un peu plus délicat. Cela nécessitera une déduction logique .
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)
$
Nous avons donc maintenant deux lignes "at". Le premier est pour cette ligne:
return args[pos].length();
et le second 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:
bar
est null
alors bar[pos]
lancera un NPE.bar[pos]
est null
alors appelée length()
, 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 le premier:
D'où bar
vient-il? Il s'agit d'un paramètre de l' test
appel de méthode, et si nous regardons comment il a test
été appelé, nous pouvons voir qu'il provient de la foo
variable statique. De plus, nous pouvons voir clairement que nous avons initialisé foo
à une valeur non nulle. Cela suffit pour écarter provisoirement cette explication. (En théorie, quelque chose d'autre pourrait changer foo
en null
... mais cela ne se produit pas ici.)
Et notre deuxième scénario? Eh bien, nous pouvons voir que pos
c'est le cas 1
, ce qui signifie que cela foo[1]
doit être null
. Est-ce possible?
En effet, ça l'est! Et voilà le problème. Lorsque nous initialisons comme ceci:
private static String[] foo = new String[2];
nous allouons un String[]
avec deux éléments qui sont initialisés ànull
. Après cela, nous n'avons pas changé le contenu de foo
... ce foo[1]
sera toujours le cas null
.
C'est comme si vous essayez d'accéder à un objet qui l'est null
. Considérez l'exemple ci-dessous:
TypeA objA;
Pour l'instant, vous venez de déclarer cet objet mais pas initialisé ni instancié . Et chaque fois que vous essayez d'accéder à une propriété ou une méthode, elle lance NullPointerException
ce qui a du sens.
Voir également cet exemple ci-dessous:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
Une exception de pointeur null est levée lorsqu'une application tente d'utiliser null dans le cas où un objet est requis. Ceux-ci inclus:
null
objet.null
objet.null
comme s'il s'agissait d'un tableau.null
comme s'il s'agissait d'une baie.null
comme s'il s'agissait d'une valeur Throwable.Les applications doivent lancer des instances de cette classe pour indiquer d'autres utilisations illégales de l' null
objet.
Référence: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
null
comme cible d'un synchronized
bloc, 2) en utilisant un null
comme cible d'un switch
, et unboxing null
.
Un null
pointeur est celui qui ne pointe nulle part. Lorsque vous déréférencer un pointeur p
, vous dites "donnez-moi les données à l'emplacement stocké dans" p ". Quand p
est un null
pointeur, l'emplacement stocké dans p
est nowhere
, vous dites" donnez-moi les données à l'emplacement "nulle part" ". De toute évidence, il ne peut pas faire cela, il lance donc a null pointer exception
.
En général, c'est parce que quelque chose n'a pas été correctement initialisé.
NULL
s'écrit comme null
en java. Et c'est une chose sensible à la casse.
De nombreuses explications sont déjà présentes pour expliquer comment cela se produit et comment y remédier, mais vous devez également suivre les meilleures pratiques pour éviter NullPointerException
tout.
Voir aussi: Une bonne liste de bonnes pratiques
J'ajouterais, très important, de bien utiliser le final
modificateur.
Utilisation du modificateur "final" chaque fois qu'il est applicable en Java
Résumé:
final
modificateur pour appliquer une bonne initialisation.@NotNull
et@Nullable
if("knownObject".equals(unknownObject)
valueOf()
plus toString()
.StringUtils
méthodes sûres nulles StringUtils.isEmpty(null)
.@Nullable
comme indiqué ci-dessus) et mettent en garde contre les erreurs potentielles. Il est également possible d'inférer et de générer de telles annotations (par exemple, IntelliJ peut le faire) sur la base de la structure de code existante.
if (obj==null)
. S'il est nul, vous devez également écrire du code pour gérer cela également.
En Java, tout (à l'exception des types primitifs) se présente sous la forme d'une classe.
Si vous souhaitez utiliser n'importe quel objet, vous avez deux phases:
Exemple:
Object object;
object = new Object();
Idem pour le concept de baie:
Item item[] = new Item[5];
item[0] = new Item();
Si vous ne donnez pas la section d'initialisation, le NullPointerException
problème survient.
Une exception de pointeur nul est un indicateur que vous utilisez un objet sans l'initialiser.
Par exemple, ci-dessous se trouve une classe d'étudiants qui l'utilisera 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 student;
public School() {
try {
student.getId();
}
catch(Exception e) {
System.out.println("Null pointer exception");
}
}
}
Parce que vous utilisez student
, mais vous avez oublié de l'initialiser comme dans le bon code ci-dessous:
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");
}
}
}
En 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 essayez d'exécuter une méthode d'objet, la référence demande à l'objet vivant d'exécuter cette méthode. Mais si la référence fait référence à NULL (rien, zéro, vide, nada), il n'y a aucun moyen d'exécuter la méthode. Ensuite, le runtime vous le fait savoir en lançant une NullPointerException.
Votre référence "pointe" vers null, donc "Null -> Pointer".
L'objet vit dans l'espace mémoire de la machine virtuelle et le seul moyen d'y accéder est d'utiliser des this
références. 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 de 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 les otherReference
deux pointent vers null) alors l'objet est "inaccessible". Il n'y a aucun moyen de travailler avec lui, donc cet objet est prêt à être récupéré, et à un moment donné, la machine virtuelle libérera la mémoire utilisée par cet objet et en allouera une autre.
Une autre occurrence de a NullPointerException
se produit lorsque l'on déclare un tableau d'objets, puis 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));
}
Ce NPE particulier peut être évité 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 commune ; pour tout type de tableau d'objets, cela signifie que tous les éléments le sont null
.
Vous devez initialiser les éléments du tableau avant d'y accéder ou de les déréférencer.
String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Optional
devait retourner null. Le mot-clé est très bien. Savoir se prémunir contre cela est essentiel. Cela en offre une occurrence courante et des moyens de l'atténuer.