Qu'est-ce qu'une NullPointerException et comment y remédier?

Oct 20 2008

Que sont les exceptions de pointeur nul ( 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 empêchiez l'exception de provoquer l'arrêt prématuré du programme?

Réponses

3848 VincentRamdhanie Oct 20 2008 at 20:54

Lorsque vous déclarez une variable de référence (c'est-à-dire un objet), vous créez réellement 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 xest un intet Java l'initialisera 0pour vous. Lorsque vous lui attribuez la valeur de 10sur la deuxième ligne, votre valeur de 10est écrite dans l'emplacement mémoire référencé 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 Integerqui 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 newmot-clé est utilisé pour instancier (ou créer) un objet de type Integer, et la variable pointeur numest affectée à cet Integerobjet.

Le NullPointerExceptionproduit lorsque vous déclarez une variable mais n'avez pas créé un objet et l' affecter à la variable avant d'utiliser le contenu de la variable (appelée déréférencement ). Vous indiquez 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 d'un champ, ou lors de l'utilisation [pour indexer un tableau.

Si vous essayez de déréférencer numAVANT de créer l'objet, vous obtenez un fichier NullPointerException. Dans les cas les plus triviaux, le compilateur détecte le problème et vous informe 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, assumes obj is not null
   obj.myMethod();
}

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, objis null, et l'instruction obj.myMethod()lèvera un NullPointerException.

Si la méthode est destinée à faire quelque chose à l'objet passé comme le fait la méthode ci-dessus, il est approprié de lancer le NullPointerExceptioncar il s'agit d'une erreur de programmeur et le programmeur aura besoin de ces informations à des fins de débogage.

En plus de NullPointerExceptions lancés en raison de la logique de la méthode, vous pouvez également vérifier les arguments de méthode pour les nullvaleurs et lancer explicitement les NPE en ajoutant quelque chose comme ce qui suit au début d'une méthode:

//Throws an NPE with a custom error message if obj is null
Objects.requireNonNull(obj, "obj must not be null");

Notez qu'il est utile d'indiquer clairement dans votre message d'erreur quel objet ne peut pas être null. L'avantage de faire une validation comme celle-ci est que 1) vous pouvez renvoyer vos propres messages d'erreur plus clairs et 2) pour le reste de la méthode, vous savez qu'à moins d' objêtre réaffecté, il n'est pas nul et peut être déréférencé en toute sécurité.

Alternativement, il peut y avoir des cas où le but de la méthode n'est pas uniquement d'opérer sur l'objet passé, et donc un paramètre nul peut être acceptable. Dans ce cas, vous devez rechercher un paramètre nul et vous comporter différemment. Vous devez également l'expliquer dans la documentation. Par exemple, doSomething()pourrait être écrit comme suit:

/**
  * @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 à l'aide de 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?

Le sondeur avec des bogues de recherche peut détecter NPE. Le sondeur peut-il intercepter dynamiquement les exceptions de pointeur nul causées par JVM

Maintenant, Java 14 a ajouté une nouvelle fonctionnalité de langage pour montrer la cause première de NullPointerException. Cette fonctionnalité linguistique fait partie de la JVM commerciale SAP depuis 2006. Ce qui suit est de 2 minutes pour comprendre cette fonctionnalité linguistique incroyable.

https://jfeatures.com/blog/NullPointerException

Dans java 14, voici un exemple de message d'exception NullPointerException:

dans le thread "main" java.lang.NullPointerException: Impossible d'appeler "java.util.List.size ()" car "list" est nul

898 BilltheLizard Oct 20 2008 at 20:20

NullPointerExceptions 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 un NullPointerException. Ce sont les plus courantes, mais d'autres méthodes sont répertoriées sur la NullPointerExceptionpage javadoc.

L'exemple de code le plus rapide que je pourrais trouver pour illustrer un NullPointerExceptionserait 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 mets explicitement la Objectréférence objégale à null. Cela signifie que j'ai une référence, mais elle ne pointe vers aucun objet. Après cela, j'essaye de traiter la référence comme si elle pointe vers un objet en appelant une méthode dessus. Cela entraîne un NullPointerExceptioncar il n'y a aucun code à exécuter à l'emplacement vers lequel pointe la référence.

(C'est un détail technique, mais je pense qu'il convient de le 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 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.)

709 fgb Jun 08 2014 at 02:22

Qu'est-ce qu'une NullPointerException?

Les JavaDocs constituent un bon point de départ . Ils ont ce couvert:

Lancé lorsqu'une application tente d'utiliser null dans un cas où un objet est requis. Ceux-ci inclus:

  • Appel de la méthode d'instance d'un objet nul.
  • Accéder ou modifier le champ d'un objet nul.
  • Prenant 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 nul.

Il est également le cas que si vous essayez d'utiliser une référence nulle avec synchronized, cela lèvera également cette exception, selon le JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • Sinon, si la valeur de l'expression est nulle, a NullPointerExceptionest levé.

Comment je le répare?

Donc, vous avez un NullPointerException. Comment y remédier? 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();
    }
}

Identifiez les valeurs nulles

La première étape consiste à identifier exactement les valeurs à l'origine de l'exception . Pour cela, nous devons effectuer un débogage. Il est important d'apprendre à lire un stacktrace . 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 lancée à la ligne 13 (dans la printStringmé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 la valeur sest nulle et l'appel de la lengthméthode lève l'exception. Nous pouvons voir que le programme arrête de lancer l'exception lorsqu'il s.length()est supprimé de la méthode.

Tracez d'où viennent ces valeurs

Vérifiez ensuite d'où vient cette valeur. En suivant les appelants de la méthode, nous voyons qu'elle sest passée avec printString(name)dans la print()méthode et qu'elle this.nameest nulle.

Tracez où ces valeurs doivent être définies

Où est this.namedéfini? Dans la setName(String)méthode. Avec un peu plus de débogage, nous pouvons voir que cette méthode n'est pas du tout appelée. Si la méthode a été appelée, assurez-vous de vérifier 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 print.

Cela suffit à nous donner une solution: ajoutez un appel à printer.setName()avant d'appeler printer.print().

Autres correctifs

La variable peut avoir une valeur par défaut (et setNamepeut empêcher qu'elle soit définie sur null):

private String name = "";

La méthode printou printStringpeut 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:

  • Vous évitez les instructions «! = Null» en Java?

Je ne trouve toujours pas le problème

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 supplémentaire, 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 d'abord le code (voir SSCCE ).

516 StephenC Jun 22 2014 at 09:16

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 les types de référence . Les types de référence en Java vous permettent d'utiliser la valeur spéciale nullqui est la manière Java de dire "pas d'objet".

A NullPointerExceptionest lancé à l'exécution chaque fois que votre programme tente d'utiliser a nullcomme s'il s'agissait d'une référence réelle. 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 étiquetée "ICI" va tenter d'exécuter la length()méthode sur une nullréférence, et cela lancera un NullPointerException.

Il existe de nombreuses façons d'utiliser une nullvaleur qui entraînera un fichier NullPointerException. En fait, les seules choses que vous pouvez faire avec un nullsans provoquer de NPE sont:

  • l'affecter à une variable de référence ou la lire à partir d'une variable de référence,
  • l'assigner à un élément du tableau ou le lire à partir d'un élément du tableau (à condition que la référence du tableau elle-même soit non nulle!),
  • passez-le en paramètre ou renvoyez-le comme résultat, ou
  • tester à l'aide des ==ou !=opérateurs, ou instanceof.

Question: Comment lire le stacktrace NPE?

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. C'est une erreur d' exécution . (Certains IDE peuvent avertir que votre programme lèvera toujours une exception ... mais le javaccompilateur standard ne le fait pas.)

Deuxième constat: lorsque je lance le programme, il sort deux lignes de "gobbledy-gook". MAL!! Ce n'est pas gobbledy-gook. C'est un stacktrace ... et il fournit des informations vitales qui vous aideront à localiser l'erreur dans votre code si vous prenez le temps de le lire attentivement.

Alors regardons 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:

  • Il vous indique le nom du thread Java dans lequel l'exception a été lancée. Pour un programme simple avec un thread (comme celui-ci), ce sera "principal". Allons-nous en ...
  • Il vous indique le nom complet de l'exception qui a été levée; ie java.lang.NullPointerException.
  • Si l'exception a un message d'erreur associé, celui-ci sera affiché après le nom de l'exception. NullPointerExceptionest inhabituel à cet égard, car il contient rarement un message d'erreur.

La deuxième ligne est la plus importante dans le diagnostic d'un NPE.

at Test.main(Test.java:4)

Cela nous dit un certain nombre de choses:

  • "at Test.main" dit que nous étions dans la mainméthode de la Testclasse.
  • "Test.java:4" donne le nom de fichier source de la classe, ET il nous dit que l'instruction où cela s'est produit est à la ligne 4 du fichier.

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 indiquera où le NPE a été lancé 1 .

En bref, la trace de 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 ...

Question: Comment rechercher la cause de l'exception NPE dans mon code?

C'est la partie difficile. La réponse courte consiste à appliquer l'inférence logique aux preuves fournies par la trace de pile, le code source et la documentation API pertinente.

Illustrons d'abord avec l'exemple simple (ci-dessus). Nous commençons par regarder la ligne que la trace de pile nous a indiquée où le NPE s'est produit:

int length = foo.length(); // HERE

Comment cela peut-il jeter un NPE?

En fait, il n'y a qu'une seule façon: cela ne peut arriver que si fooa la valeur null. Nous essayons ensuite d'exécuter la length()méthode nullet ... 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é lancée dans une ligne de la java.lang.Stringclasse et la ligne 4 de Test.javaserait la deuxième ligne «at».

Alors d'où cela nullvient-il? Dans ce cas, c'est évident et ce que nous devons faire pour y remédier est évident. (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) $ 

Alors maintenant, nous avons deux lignes «at». Le premier concerne 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 manières:

  • Si la valeur de barest nullalors bar[pos]lancera un NPE.
  • Si la valeur de bar[pos]est nullalors appeler length(), il 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ù barvient-il? C'est un paramètre de l' testappel de méthode, et si nous regardons comment a testété appelé, nous pouvons voir qu'il provient de la foovariable 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.)

Alors qu'en est-il de notre deuxième scénario? Eh bien, nous pouvons voir que posc'est 1, donc cela 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 a String[]avec deux éléments qui sont initialisésnull . Après cela, nous n'avons pas changé le contenu de foo... il le foo[1]sera toujours null.

432 RakeshBurbure Oct 20 2008 at 20:21

C'est comme si vous tentiez d'accéder à un objet qui l'est null. Considérez l'exemple ci-dessous:

TypeA objA;

À ce stade, 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, cela lancera NullPointerExceptionce qui a du sens.

Voir également cet exemple ci-dessous:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
365 nathan1138 Apr 17 2013 at 09:57

Une exception de pointeur null est levée lorsqu'une application tente d'utiliser null dans un cas où un objet est requis. Ceux-ci inclus:

  1. Appel de la méthode d'instance d'un nullobjet.
  2. Accéder ou modifier le champ d'un nullobjet.
  3. Prenant la longueur de nullcomme s'il s'agissait d'un tableau.
  4. Accéder ou modifier les emplacements de nullcomme s'il s'agissait d'un tableau.
  5. Lancer nullcomme 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' nullobjet.

Référence: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

341 MrZebra Oct 20 2008 at 20:25

Un nullpointeur 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 pest un nullpointeur, l'emplacement stocké pest nowhere, vous dites" donnez-moi les données à l'emplacement "nulle part" ". De toute évidence, il ne peut pas faire cela, donc il lance un null pointer exception.

En général, c'est parce que quelque chose n'a pas été initialisé correctement.

329 L.G. Jun 25 2014 at 18:17

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 les NullPointerExceptions.

Voir aussi: Une bonne liste de bonnes pratiques

J'ajouterais, très important, faire un bon usage du finalmodificateur. Utilisation du modificateur "final" le cas échéant en Java

Résumé:

  1. Utilisez le finalmodificateur pour appliquer une bonne initialisation.
  2. Évitez de renvoyer null dans les méthodes, par exemple renvoyer des collections vides le cas échéant.
  3. Utilisez des annotations @NotNullet@Nullable
  4. Échouez rapidement et utilisez des assertions pour éviter la propagation d'objets nuls dans toute l'application alors qu'ils ne devraient pas être nuls.
  5. Utilisez d'abord égal à un objet connu: if("knownObject".equals(unknownObject)
  6. Préfère valueOf()plus toString().
  7. Utilisez des StringUtilsméthodes sûres nulles StringUtils.isEmpty(null).
  8. Utilisez Java 8 Facultatif comme valeur de retour dans les méthodes, la classe Optional fournit une solution pour représenter des valeurs facultatives au lieu de références nulles.
323 ashishbhatt Jan 28 2012 at 13:45

En Java, tout (à l'exception des types primitifs) est sous la forme d'une classe.

Si vous souhaitez utiliser un objet, vous avez deux phases:

  1. Déclarer
  2. Initialisation

Exemple:

  • Déclaration: Object object;
  • Initialisation: object = new Object();

Idem pour le concept de tableau:

  • Déclaration: Item item[] = new Item[5];
  • Initialisation: item[0] = new Item();

Si vous ne donnez pas la section d'initialisation, le NullPointerExceptionproblème survient.

322 javidpiprani Sep 24 2013 at 13:01

Une exception de pointeur null est un indicateur que vous utilisez un objet sans l'initialiser.

Par exemple, ci-dessous est 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 que vous avez oublié de l'initialiser comme dans le code correct 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");
        }
    }
}
315 OscarRyz Oct 21 2008 at 03:05

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, void, nada), il n'y a aucun moyen pour la méthode d'être exécutée. Ensuite, le runtime vous l'a 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 VM et le seul moyen d'y accéder consiste à utiliser des thisréférences. Prenons 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 referenceet les otherReferencedeux 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 VM libèrera la mémoire utilisée par cet objet et en allouera une autre.

287 Makoto May 25 2014 at 13:11

Une autre occurrence de a NullPointerExceptionse produit lorsque l'on déclare un tableau d'objets, puis essaie immédiatement de déréférencer les é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, utilisation .equalssur 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 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));
}