Question

Quelles sont les exceptions Null Pointer ( java.lang.NullPointerException ) et quelles en sont les causes?

Quels 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?

Était-ce utile?

La solution

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 un int et Java l'initialise à 0 pour vous. Lorsque vous lui affectez la valeur 10 sur la deuxième ligne, votre valeur 10 est écrite dans l'emplacement mémoire désigné par x .

Toutefois, lorsque vous essayez de déclarer un type de référence , un événement 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). Comme vous n'avez pas encore dit à quoi pointer, Java le définit sur null , ce qui signifie " je pointe vers 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é à 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 une NullPointerException . Dans les cas les plus triviaux, le compilateur détecte le problème et vous informe que " num n'a peut-être pas été initialisé ,". 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 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 convient de lancer NullPointerException car il s’agit d’une erreur de programmation et le programmeur aura besoin de ces informations à des fins de débogage.

Sinon, il peut arriver que le but de la méthode ne soit pas uniquement d’opérer sur l’objet transmis, et qu’un paramètre nul puisse 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.

/**
  * @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 & amp; provoquer l’utilisation de Stack Trace

Autres conseils

Les

NullPointerException 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 null ou essayer d'accéder à un champ d'une référence null déclenchera une NullPointerException . Celles-ci sont les plus courantes, mais d’autres méthodes sont répertoriées dans la NullPointerException page javadoc.

L’exemple de code le plus rapide que je pourrais trouver pour illustrer une NullPointerException serait probablement:

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 Objet obj à 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 entraîne une NullPointerException car il n'y a pas de code à exécuter à l'emplacement indiqué 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 littéralement nulle part) , ce qui est légèrement différent de celui de pointer sur un emplacement invalide.)

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   l'objet est requis. Ceux-ci incluent:

     
      
  • 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 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 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 synchronisé , cette exception générera également cette exception, selon le JLS :

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

Comment puis-je résoudre ce problème?

Vous avez donc une NullPointerException . Comment corrigez-vous cela? Prenons un exemple simple qui lève une 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 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 à la ligne 13 (dans la méthode printString ). 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 est null et que l'appel de la méthode length dessus lève l'exception. Nous pouvons voir que le programme cesse de lever l'exception lorsque s.length () est supprimé de la méthode.

Tracez l'origine de 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 < code> this.name est null.

Indiquez où ces valeurs doivent être définies

this.name est-il 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 l'ordre afin que ces méthodes soient appelées et que la méthode set ne s'appelle pas après la méthode print.

Cela suffit pour 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 nomFichier peut l'empêcher d'être définie sur null):

private String name = "";

La méthode print ou printString peut rechercher la valeur null , par exemple:

printString((name == null) ? "" : name);

Ou vous pouvez concevoir la classe de sorte que nom 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 aussi:

Je ne trouve toujours pas 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 commencer par simplifier le code (voir SSCCE ).

Question: Quelle est la cause d'une NullPointerException (NPE)?

Comme vous le savez sûrement, les types Java sont divisés en types primitifs ( boolean , int , etc.) et en types de référence . Les types de référence en Java vous permettent d'utiliser la valeur spéciale null , qui est le moyen utilisé par Java pour indiquer "aucun objet".

Une NullPointerException est émise à 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
    }
}

la déclaration intitulée "ICI" va essayer d’exécuter la méthode length () sur une référence null , et cela déclenchera une NullPointerException .

Vous pouvez utiliser une valeur null de différentes manières pour générer une 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 comme résultat, ou
  • testez-le à l'aide des opérateurs == ou ! = = / / code> 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. Il s’agit d’une erreur runtime . (Certains IDE peuvent vous avertir que votre programme lèvera toujours une exception ... mais pas le compilateur javac standard.)

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 une pile ... et elle fournit des informations vitales qui vous aideront à localiser l'erreur dans votre code, si vous prenez le temps de la lire attentivement.

Alors regardons ce qu'il dit:

Exception in thread "main" java.lang.NullPointerException

La première ligne de la trace de la pile vous 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), il sera "principal". Passons à autre chose ...
  • Il vous indique le nom complet de l'exception qui a été levé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 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 dit un certain nombre de choses:

  • " at Test.main " indique 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 symbole "ICI". commentaire.

Notez que dans un exemple plus compliqué, 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 "en") vous indiquera où le NPE a été lancé 1 .

En bref, la trace de la pile nous indiquera sans ambiguïté quelle instruction du programme a lancé le NPE.

1 - Ce n'est 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 la pile, le code source et la documentation de l'API correspondante.

Illustrons d’abord avec 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 toto 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 passera-t-il si le NPE était jeté dans l'appel de méthode length () ?

Eh bien, si cela se produisait, la trace de la pile aurait un aspect différent. Le premier " at " line dira que l'exception a été levée dans une ligne de la classe java.lang.String et que la ligne 4 de Test.java sera le deuxième " at " ligne.

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 à toto .)

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 "at" dans " lignes. Le premier concerne cette ligne:

return args[pos].length();

et le second concerne 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 , l'appel de length () renverra alors 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 la barre ? C'est un paramètre pour l'appel de la méthode test , et si nous regardons comment test a été appelé, nous pouvons voir qu'il provient du foo variable statique. De plus, nous pouvons voir clairement que nous avons initialisé toto à une valeur non nulle. Cela suffit pour écarter provisoirement cette explication. (En théorie, autre chose 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 toto [1] doit être null . Est-ce possible?

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

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

TypeA objA;

À ce stade, 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 de celle-ci, il générera une NullPointerException qui a du sens.

Voir aussi l'exemple ci-dessous:

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

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

  1. Appel de la méthode d'instance d'un objet null .
  2. Accès ou modification du 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

Un pointeur null en est un 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 nulle part , vous dites "donner". moi les données à l'emplacement «nulle part» " ;. Évidemment, il ne peut pas faire cela, alors il lève une exception un pointeur nul .

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

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

Voir aussi: Une bonne liste des meilleures pratiques

J'ajouterais, très important, de bien utiliser le modificateur final . Utiliser le " final " modificateur, 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, si vous le souhaitez, les collections vides
  3. Utiliser les annotations @NotNull et @Nullable
  4. Échec rapide et utilisez des assertions pour éviter la propagation des objets null dans l'ensemble de l'application lorsqu'ils ne devraient pas être nuls.
  5. Commencez par utiliser égal à un objet connu: if ("quotObject connu" .equals (unknownObject)
  6. Préférez valueOf () à toString ().
  7. Utiliser les méthodes null code StringUtils StringUtils.isEmpty (null) .

Une exception de pointeur nul est un indicateur du fait que vous utilisez un objet sans l'avoir initialisé.

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 étudiant , mais vous avez oublié de l'initialiser comme dans le code correct affiché 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, 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: objet d'objet;
  • Initialisation: objet = nouvel objet ();

Idem pour le concept de tableau:

  • Déclaration: Elément [] = nouvel élément [5];
  • Initialisation: élément [0] = nouvel élément ();

Si vous ne donnez pas la section d'initialisation, la NullPointerException est générée.

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 le fait savoir en lançant une exception NullPointerException.

Votre référence est "pointant". pour annuler, donc "Null - > Pointeur ".

L'objet réside dans l'espace mémoire de la machine virtuelle et le seul moyen d'y accéder consiste à utiliser 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 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 - 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), puis l'objet est "inaccessible". Nous ne pouvons absolument pas travailler avec lui. Cet objet est prêt à être récupéré. À un moment donné, la machine virtuelle libère la mémoire utilisée par cet objet et en alloue un autre.

Une autre occurrence d'une 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 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 les 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));
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top