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


Quelles sont les exceptions de pointeur Null (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 la fin prématurée du programme?

Author: Ziggy , 2008-10-20

12 answers

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érons 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 affectez à 10 dans la deuxième ligne de votre valeur 10 est écrit dans l'emplacement mémoire pointé 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 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 vers Java le définit sur null, ce qui signifie "Je ne pointe 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 à ceci objet. Vous pouvez maintenant référencer l'objet en utilisant l'opérateur de déréférencement . (un point).

Le Exception que vous avez demandé se produit lorsque vous déclarez une variable mais que vous n'avez pas créé d'objet. 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 attrapera le problème et vous fera savoir que "num n'a peut-être pas été initialisé" mais parfois vous écrivez du code qui ne crée pas directement l'objet.

Pour exemple, vous pouvez avoir une méthode comme suit:

public void doSomething(SomeObject obj) {
   //do something to obj
}

Auquel cas vous ne créez pas l'objet obj, en supposant plutôt qu'il a été créé avant l'appel de la méthode doSomething. 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 transmis, il convient de lancer le NullPointerException car c'est une erreur de programmeur et le programmeur aura besoin de ces informations pour le débogage but.

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 null peut être acceptable. Dans ce cas, vous devrez vérifier un paramètre null et se comporter différemment. Vous devriez également expliquer cela dans la documentation. Par exemple, doSomething pourrait être écrit 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 pile

 3287
Author: Vincent Ramdhanie, 2018-05-12 04:34:24

NullPointerExceptions sont des exceptions qui se produisent lorsque vous essayez d'utiliser une référence qui 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 null ou essayer d'accéder à un champ d'une référence null déclenchera un NullPointerException. Ce sont les plus courants, mais d'autres moyens sont répertoriés sur le NullPointerException page javadoc.

Probablement l'exemple de code 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 à l'intérieur de main, je définis explicitement la référence Object obj égale à null. Cela signifie que j'ai une référence, mais elle ne pointe vers aucun objet. Après cela, j'essaie de traiter la référence comme si elle pointait 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 pointé par la référence.

(C'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 mémoire non valide. Un pointeur null ne pointe littéralement pas n'importe où , ce qui est subtilement différent de pointer vers un emplacement qui se trouve être invalide.)

 777
Author: Bill the Lizard, 2017-12-25 14:24:54

Qu'est-ce qu'une NullPointerException?

Un bon point de départ est le JavaDocs. Ils ont ce couvert:

Lancé lorsqu'une application tente d'utiliser null dans un cas où l'objet est nécessaire. Ceux-ci comprennent:

  • Appel de 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 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 lancer des instances de cette classe pour indiquer d'autres utilisations illégales de l'objet null.

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

SynchronizedStatement:
    synchronized ( Expression ) Block
  • Sinon, si la valeur de l'Expression est null, NullPointerException est levée.

Comment puis-je le réparer?

Donc vous avez un NullPointerException. Comment voulez-vous résoudre ce problème? 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 null

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)

Ici, nous voyons que le l'exception est levée sur la ligne 13 (dans la méthode printString). Regardez la ligne et vérifiez quelles valeurs sont nulles par ajout d'instructions de journalisation ou utilisation d'un débogueur . Nous découvrons que s est null, et appeler la méthode length sur elle lève l'exception. Nous pouvons voir que le programme cesse de lancer l'exception lorsque s.length() est supprimé de la méthode.

Trace d'où viennent ces valeurs

Ensuite, vérifiez d'où vient cette valeur. Par suite de la les appelants de la méthode, nous voyons que s est transmis avec printString(name) dans le print() méthode, et this.name est nulle.

Trace où ces valeurs devraient ê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, assurez-vous de vérifier la commande que ces méthodes sont appelées, et l'ensemble de la méthode 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().

Autres corrections

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

private String name = "";

Le print ou printString méthode peut vérifier la valeur 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. Au minimum, inclut le stacktrace dans la question, et marque les numéros de ligne importants dans le code. Essayez également de simplifier le code en premier (voir SSCCE).

 627
Author: fgb, 2017-12-29 13:53:51

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 "pas d'objet".

Un NullPointerException est lancé à 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
    }
}

La déclaration étiquetée "HERE" va tenter d'exécuter la méthode length() sur une référence null, et cela lancera un NullPointerException.

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

  • affecter à une variable de référence ou de lire à partir d'une variable de référence,
  • attribuez-le à un élément de tableau ou lisez-le à partir d'un élément de tableau (à condition que la référence du tableau lui-même est non-nulle!),
  • passez - le comme paramètre ou renvoyez-le comme résultat, ou
  • testez-le en utilisant les opérateurs == ou !=, ou instanceof.

Question: Comment puis-je lire le 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. (CertainsEs peuvent avertir que votre programme lancera toujours un exception ... mais le compilateur standard javac ne le fait pas.)

Deuxième observation: lorsque j'exécute le programme, il génère deux lignes de "gobbledy-gook". FAUX!!, ce Qui n'est pas charabia. C'est une stacktrace ... et il fournit des informations vitales qui vous aideront à traquer 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 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 fil (comme celui-ci), il sera "principal". Passons sur ...
  • , Il vous indique le nom complet de l'exception qui a été levée; c'est à dire java.lang.NullPointerException.
  • Si l'exception a un message d'erreur associé, celui-ci sera affiché après le nom de l'exception. NullPointerException est inhabituel à cet égard, car il a rarement un message d'erreur.

La deuxième ligne est le plus important dans le diagnostic d'un 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 classe Test.
  • "Test.java: 4 " donne le nom de fichier source de la classe, ET il nous indique 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 plus exemple 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 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 puis-je retrouver la cause de l'exception NPE dans mon code?

C'est la partie la plus 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 avec l'exemple simple (ci-dessus). Nous commençons par regarder la ligne que la trace de la pile nous a dit 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 ensuite, essayez 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é lancé à l'intérieur de l'appel de méthode length()?

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 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, 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:

  • Si la valeur de bar est null alors bar[pos] lancera un NPE.
  • Si la valeur de bar[pos] est null puis appeler length() dessus lancera 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 statique foo. De plus, nous pouvons voir clairement que nous avons initialisé foo à une valeur non nulle. C'est suffisant pour écarter provisoirement cette explication. (En théorie, quelque chose d'autre pourrait - modifier foo de 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 qu' foo[1] doit être null. Est-ce possible?

En effet, il est! Et c'est 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ées à null. Après que, nous n'avons pas changé le contenu de foo ... donc foo[1] sera toujours null.

 448
Author: Stephen C, 2018-03-19 14:05:30

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

TypeA objA;

En ce moment, vous venez de déclarercet objet mais pas initialisé ou instancié. Et chaque fois que vous essayez d'accéder à une propriété ou une méthode, il lancera NullPointerException ce qui a du sens.

Voir aussi cet exemple ci-dessous:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
 374
Author: Rakesh Burbure, 2018-05-15 14:58:39

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 comprennent:

  1. Appelant la méthode d'instance d'un objet null.
  2. Accéder ou modifier le champ d'un objet null.
  3. Prenant 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.

Demandes devrait 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

 322
Author: nathan1138, 2017-12-20 21:40:50

Un NULL pointeur qui pointe vers 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é dans p est nowhere, vous dites "donnez-moi les données à l'emplacement"nulle part"". Évidemment, 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.

 299
Author: MrZebra, 2017-12-25 14:30:50

Beaucoup d'explications sont déjà présentes pour expliquer comment cela se passe et comment le réparer, mais vous devez également suivre les 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. En utilisant le modificateur" final " chaque fois qu'il est applicable en Java

Résumé:

  1. Utilisez le modificateur final pour 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. Échoue rapidement et utilise assertions pour éviter la propagation d'objets null à travers toute l'application alors qu'ils ne devraient pas être nuls.
  5. Use equals avec un objet connu en premier: if("knownObject".equals(unknownObject)
  6. Préfère valueOf() à toString().
  7. Utilisez les méthodes null safe StringUtils StringUtils.isEmpty(null).
 285
Author: L. G., 2017-12-25 14:31:46

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

Par exemple, voici 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 null.

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 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 ");
        }
    }
}
 283
Author: javid piprani, 2017-12-20 21:44:41

En Java, tout est sous la forme d'une classe.

Si vous voulez utiliser n'importe quel objet, vous avez deux phases:

  1. Déclarer
  2. Initialisation

Exemple:

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

Idem pour le concept de tableau

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

Si vous ne donnez pas la section d'initialisation, le NullpointerException se pose.

 275
Author: ashish bhatt, 2018-06-22 00:34:42

Dans 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 (nothing, zero, void, nada), il n'y a aucun moyen que la méthode soit exécutée. Ensuite, le runtime vous le fait savoir en lançant une NullPointerException.

Votre référence est "pointant" vers null, donc "Null - > Pointeur".

L'objet vit dans l'espace mémoire de la machine virtuelle et le seul moyen d'y accéder est d'utiliser des 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 pointent tous deux vers null) alors l'objet est "inaccessible". Il n'y a aucun moyen de travailler avec, donc cet objet est prêt à être collecté, et à à un moment donné, la machine virtuelle libérera la mémoire utilisée par cet objet et en allouera un autre.

 265
Author: OscarRyz, 2018-08-10 07:52:57

Une Autre occurrence de 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 d'elle.

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 non nul garanti.

Tous les éléments à l'intérieur d'un tableau sont initialisés à leur valeur initiale; pour tout type d'objet tableau, cela signifie que tous les éléments sont null.

Vous devez initialisez 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));
}
 252
Author: Makoto, 2017-12-25 14:35:15