web-dev-qa-db-fra.com

Qu'est-ce qu'une exception NullPointerException et comment la résoudre?

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 d'empêcher l'exception de provoquer l'arrêt prématuré du programme?

210
Ziggy

Lorsque vous déclarez une variable de référence (c'est-à-dire un objet), vous créez réellement un pointeur sur un objet. Considérez le code suivant dans lequel 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 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 une référence , tapez , 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 à quoi pointer, Java le définit sur null, ce qui signifie " que je désigne rien ".

Dans la deuxième ligne, le mot clé new est utilisé pour instancier (ou créer) un objet de type Integer et la variable de pointeur num est affectée à cet objet Integer.

La NullPointerException se produit lorsque vous déclarez une variable sans créer d'objet. Vous pointez donc vers quelque chose qui n'existe pas réellement.

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étecte le problème et vous indique que "num may not have been initialized", mais vous pouvez parfois é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 qu'il a été créé avant l'appel de la méthode doSomething(). Notez qu'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 à modifier l’objet transmis, il est approprié d’envoyer le NullPointerException car c’est une erreur de programmation et le programmeur aura besoin de ces informations à des fins de débogage.

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 qu’un paramètre nul peut donc être acceptable. Dans ce cas, vous devez rechercher un paramètre null et vous comporter différemment. Vous devriez également expliquer cela 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 avec Stace Trace

3604
Vincent Ramdhanie

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 s'il s'agissait d'un objet de référence. L'appel d'une méthode sur une référence null ou la tentative d'accès à un champ d'une référence null déclenchera un NullPointerException. Celles-ci sont les plus courantes, mais d’autres méthodes sont répertoriées sur la page NullPointerException javadoc.

Le code d'exemple le plus rapide que je pourrais 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 de main, je mets explicitement la référence Object à obj égale à null. Cela signifie que j'ai une référence, mais elle ne pointe pas vers un objet. Après cela, j'essaie de traiter la référence comme si elle pointait sur un objet en appelant une méthode dessus. Cela se traduit par un NullPointerException car il n'y a pas de code à exécuter à l'emplacement pointé par la référence.

(C’est un détail technique, mais je pense qu’il convient de mentionner: une référence pointant sur null n’est pas identique à un pointeur C pointant vers un emplacement mémoire invalide. Un pointeur nul ne pointe pas littéralement n'importe où , ce qui est légèrement différent de celui de pointer sur un emplacement invalide.)

845
Bill the Lizard

Qu'est-ce qu'une exception NullPointerException?

Un bon point de départ est le JavaDocs . Ils ont ceci 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 null.
  • Accéder ou modifier le champ d'un objet null.
  • Prendre la longueur de null comme s'il s'agissait d'un tableau.
  • Accéder ou modifier les slots de null comme s'il s'agissait d'un tableau.
  • Lancer null comme s'il s'agissait d'une valeur Throwable.

Les applications doivent utiliser des instances de cette classe pour indiquer d'autres utilisations illégales de l'objet null.

Il est également vrai que si vous essayez d'utiliser une référence null avec synchronized, cette exception lèvera également cette exception, pour le JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • Sinon, si la valeur de l'expression est nulle, un NullPointerException est renvoyé.

Comment je le répare?

Donc vous avez un NullPointerException. Comment corrigez-vous cela? 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();
    }
}

Identifie les valeurs nulles

La première étape consiste à identifier exactement quelles valeurs sont à l'origine de l'exception. Pour cela, nous devons faire du 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)

Nous voyons ici que l'exception est levée sur la ligne 13 (dans la méthode printString). Regardez la ligne et vérifiez quelles valeurs sont nulles en ajoutant instructions de consignation ou en utilisant un débogueur. Nous découvrons que s est nul et que l'appel de la méthode length lève l'exception. Nous pouvons voir que le programme cesse de lever l'exception lorsque s.length() est supprimé de la méthode.

Trace d'où proviennent ces valeurs

Vérifiez ensuite d'où provient cette valeur. En suivant les appelants de la méthode, nous voyons que s est passé avec printString(name) dans la méthode print() et que this.name est nul.

Trace où ces valeurs doivent être définies

Où est this.name défini? 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 du tout. Si la méthode a été appelée, vérifiez que order indique que ces méthodes sont appelées et que la méthode set n'est pas appelée after la méthode print.

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

Autres corrections

La variable peut avoir un valeur par défaut ​​(et setName peut l'empêcher d'être 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 que nameait 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 n'arrive toujours pas à trouver le problème

Si vous avez essayé de résoudre le problème et que vous n'avez toujours pas de solution, vous pouvez poser 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 d’abord le code (voir SSCCE ).

674
fgb

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 dans Java vous permettent d'utiliser la valeur spéciale null, qui est la manière Java de dire "pas d'objet".

Un NullPointerException est lancé au moment de l'exécution chaque fois que votre programme tente d'utiliser un null comme 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 intitulée "ICI" va tenter d'exécuter la méthode length() sur une référence null, ce qui jettera un NullPointerException.

Vous pouvez utiliser une valeur null de plusieurs façons pour obtenir un NullPointerException. En fait, les seules choses que vous pouvez faire avec un null sans provoquer de NPE sont les suivantes:

  • assignez-le à une variable de référence ou lisez-le à partir d'une variable de référence,
  • l'assigner à un élément de tableau ou le lire dans un élément de tableau (à condition que la référence de tableau elle-même ne soit pas nulle!),
  • transmettez-le en tant que paramètre ou renvoyez-le à la suite, ou
  • testez-le à l'aide des opérateurs == ou != 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 vous avertir que votre programme lèvera toujours une exception ... mais pas le compilateur standard javac.)

Deuxième observation: lorsque je lance le programme, il génère deux lignes de "gobbledy-gook". FAUX !! 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. soigneusement.

Alors regardons ce qu'il dit:

Exception in thread "main" Java.lang.NullPointerException

La première ligne de la trace de la pile vous indique un certain nombre de choses:

  • Il vous indique le nom du thread Java dans lequel l'exception a été levée. Pour un programme simple avec un thread (comme celui-ci), ce sera "main". Allons-nous en ...
  • Il vous indique le nom complet de l'exception qui a été lancée. c'est-à-dire Java.lang.NullPointerException.
  • Si un message d'erreur est associé à l'exception, il sera affiché après le nom de l'exception. NullPointerException est inhabituel à cet égard, car il comporte 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 dit un certain nombre de choses:

  • "at Test.main" dit que nous étions dans la méthode main de la classe Test.
  • "Test.Java:4" donne le nom de fichier source de la classe ET il nous dit que la déclaration où cela s'est produit se trouve à la ligne 4 du fichier.

Si vous comptez les lignes du fichier ci-dessus, la ligne 4 est celle que j'ai étiquetée avec le commentaire "ICI".

Notez que dans un exemple plus complexe, la trace de la pile NPE comportera de nombreuses lignes. 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 déclaration du programme a lancé le NPE.

1 - Pas tout à fait vrai. Il y a des choses appelées exceptions imbriquées ...

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

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 de l'API pertinente.

Illustrons d'abord par l'exemple simple (ci-dessus). Nous commençons par examiner la ligne indiquée par la trace de la pile à l'endroit où le NPE s'est passé:

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

Comment cela peut-il lancer un NPE?

En fait, il n'y a qu'un moyen: cela 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 se passe-t-il si le NPE a été jeté dans l'appel de méthode length()?

Eh bien, si cela se produisait, la trace de la pile aurait un aspect différent. La première ligne "at" indiquerait que l'exception a été renvoyée dans une ligne de 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, c'est évident, et ce que nous devons faire pour le résoudre est évident. (Attribuez une valeur non nulle à foo.)

OK, essayons donc un exemple un peu plus compliqué. 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 manières:

  • Si la valeur de bar est null, alors bar[pos] lancera un NPE.
  • Si la valeur de bar[pos] est null, l'appel de length() dessus enverra un NPE.

Ensuite, nous devons déterminer lequel de ces scénarios explique ce qui se passe réellement. Nous allons commencer par explorer le premier:

D'où vient bar? C'est un paramètre de l'appel de méthode test, et si nous regardons comment test a été appelé, nous pouvons voir qu'il provient de la variable foo 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 pos est 1, ce qui signifie que foo[1] doit être null. Est-ce possible?

En effet, ça l'est! Et voilà le problème. Quand on initialise comme ça:

private static String[] foo = new String[2];

nous allouons un String[] avec deux éléments initialisés à null. Après cela, nous n'avons pas modifié le contenu de foo ... donc foo[1] restera toujours null.

487
Stephen C

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

TypeA objA;

A ce moment vous venez de déclarer cet objet mais pas initialisé ou instancié . Et chaque fois que vous essayez d’accéder à une propriété ou à une méthode qu’elle contient, elle jette NullPointerException ce qui a un sens.

Voir aussi cet exemple ci-dessous:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
409
Jorge Ferreira

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

  1. Appel de la méthode d'instance d'un objet null.
  2. Accéder ou modifier le champ d'un objet null.
  3. Prendre la longueur de null comme s'il s'agissait d'un tableau.
  4. Accéder ou modifier les emplacements de null comme s'il s'agissait d'un tableau.
  5. Lancer null comme s'il s'agissait d'une valeur Throwable.

Les applications doivent utiliser 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

348
nathan1138

Un pointeur null en est un qui ne pointe nulle part. Lorsque vous déréférencez un pointeur p, vous dites "donnez-moi les données à l'emplacement stocké dans" p ". Lorsque p est un pointeur null, l'emplacement stocké à p is nowhere, vous dites "donnez-moi les données à l'emplacement" nulle part "". Évidemment, cela ne peut pas être fait, alors il jette un null pointer exception.

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

321
MrZebra

De nombreuses explications sont déjà présentes pour expliquer comment cela se passe et comment y remédier, mais vous devez également suivre les meilleures pratiques pour éviter NullPointerExceptions du tout.

Voir aussi: ne bonne liste de bonnes pratiques

J'ajouterais, très important, de bien utiliser le modificateur final. tilisation du modificateur "final" le cas échéant en Java

Résumé:

  1. Utilisez le modificateur final pour appliquer une bonne initialisation.
  2. Évitez de renvoyer null dans les méthodes, par exemple, en renvoyant des collections vides le cas échéant.
  3. Utiliser les annotations @NotNull et @Nullable
  4. Fail fast et utilisez des assertions pour éviter la propagation des objets null dans l’ensemble de l’application alors qu’ils ne devraient pas être nuls.
  5. Utilisez d’abord un objet connu avec un objet connu: if("knownObject".equals(unknownObject)
  6. Préférez valueOf() sur toString ().
  7. Utilisez les méthodes null safe StringUtilsStringUtils.isEmpty(null).
308
L. G.

Une exception de pointeur nul indique que vous utilisez un objet sans l'initialiser.

Par exemple, ci-dessous est une classe d’élèves 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 null.

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 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");
        }
    }
}
304
javid piprani

En Java, tout (à l'exception des types primitifs) se présente 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, la NullPointerException apparaît.

301
ashish bhatt

Dans Java , toutes les variables que vous déclarez sont en réalité 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), la méthode n'est en aucun cas exécutée. Ensuite, le moteur d'exécution vous a fait savoir cela en lançant une exception NullPointerException.

Votre référence est "pointant" sur null, donc "Null -> Pointer".

L'objet vit dans l'espace mémoire VM et le seul moyen d'y accéder consiste à utiliser les références this. 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 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 - lorsqu'il n'y a plus de références à un objet (dans l'exemple ci-dessus, lorsque reference et otherReference désignent tous deux la valeur null), l'objet est "inaccessible". Nous ne pouvons absolument pas travailler avec cet objet. Cet objet est prêt à être récupéré. À un moment donné, le VM libère la mémoire utilisée par cet objet et en alloue un autre.

297
OscarRyz

Une autre occurrence d'un NullPointerException se produit lorsque l'on déclare un tableau d'objets, puis tente 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, utilisez .equals sur un objet garanti non nul.

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 de pouvoir y accéder ou 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));
}
275
Makoto