209 votes

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

Que sont les exceptions liées aux pointeurs nuls ( java.lang.NullPointerException ) et quelles en sont les causes ?

Quels sont les méthodes/outils à utiliser pour déterminer la cause afin d'empêcher l'exception de provoquer l'arrêt prématuré du programme ?

4087voto

Vincent Ramdhanie Points 46265

Lorsque vous déclarez une variable de référence (c'est-à-dire un objet), vous créez en réalité 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 x est un int et Java l'initialisera à 0 pour vous. Lorsque vous attribuez la valeur de 10 sur la deuxième ligne, votre valeur de 10 est écrit dans l'emplacement mémoire auquel se réfère x .

Mais, lorsque vous essayez de déclarer une référence type 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 il ne contient pas encore de valeur primitive. Au lieu de cela, il contient un pointeur (parce que le type est Integer qui est un type de référence). Puisque vous n'avez pas encore dit ce qu'il faut pointer, Java le fixe à null ce qui signifie " J'indique rien ".

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

El NullPointerException (NPE) se produit lorsque vous déclarez une variable mais que vous n'avez pas créé un objet et ne l'avez pas assigné à la variable avant d'essayer d'utiliser le contenu de la variable (appelé déréférencement ). Vous pointez donc du doigt quelque chose qui n'existe pas réellement.

Le déréférencement se produit généralement lors de l'utilisation de . pour accéder à une méthode ou à un champ, ou en utilisant [ pour indexer un tableau.

Si vous tentez de déréférencer num avant en créant l'objet, vous obtenez un NullPointerException . Dans les cas les plus triviaux, le compilateur détectera le problème et vous fera savoir que " num may not have been initialized mais parfois vous pouvez écrire du code qui ne crée pas directement l'objet.

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

public void doSomething(SomeObject obj) {
   // Do something to obj, assumes obj is not null
   obj.myMethod();
}

Auquel cas, vous ne créez pas l'objet obj mais plutôt en supposant qu'il a été créé avant la date d'entrée en vigueur de l'accord. doSomething() a été appelée. Notez qu'il est possible d'appeler la méthode comme ceci :

doSomething(null);

Dans ce cas, obj est null et la déclaration obj.myMethod() lancera un NullPointerException .

Si la méthode a pour but de faire quelque chose à l'objet transmis, comme le fait la méthode ci-dessus, il est approprié de lancer la commande NullPointerException car il s'agit d'une erreur de programmation et le programmeur aura besoin de cette information à des fins de débogage.

En plus de NullPointerException lancés à la suite de la logique de la méthode, vous pouvez également vérifier les arguments de la méthode à la recherche de null et lancer des NPE explicitement en ajoutant quelque chose comme ce qui suit près du 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 dont ne peut pas être null . L'avantage de valider ceci est que 1) vous pouvez renvoyer vos propres messages d'erreur plus clairs et 2) pour le reste de la méthode, vous savez que, à moins que obj est réaffecté, il n'est pas nul et peut être déréférencé en toute sécurité.

Par ailleurs, il peut y avoir des cas où le but de la méthode n'est pas uniquement d'agir sur l'objet transmis, et où un paramètre nul peut donc être acceptable. Dans ce cas, il faut vérifier que la méthode ne contient pas un paramètre nul et se comportent 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 d'arrêter d'empêcher l'exception de provoquer l'arrêt prématuré du programme ?

Sonar avec find bugs peut détecter les NPE. Sonar peut-il attraper les exceptions de pointeur nul causées par la JVM de façon dynamique ?

Maintenant, Java 14 a ajouté une nouvelle fonctionnalité linguistique pour montrer la cause première de l'exception NullPointerException. Cette fonctionnalité linguistique fait partie de la JVM commerciale de SAP depuis 2006.

En Java 14, voici un exemple de message d'exception NullPointerException :

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

606 votes

"La meilleure façon d'éviter ce type d'exception est de toujours vérifier la présence de null lorsque vous n'avez pas créé l'objet vous-même." Si l'appelant passe null, mais que null n'est pas un argument valide pour la méthode, alors il est correct de renvoyer l'exception à l'appelant car c'est la faute de ce dernier. Ignorer silencieusement les entrées invalides et ne rien faire dans la méthode est un très mauvais conseil car il cache le problème.

117 votes

J'ajouterais une remarque sur ce post expliquant que même les assignations aux primitives peuvent causer des NPEs lors de l'utilisation de l'autoboxing : int a=b peut lancer un NPE si b est un Integer . Dans certains cas, cela peut être source de confusion pour le débogage.

66 votes

Est-il possible de capturer les NPE lancés par une application Web à partir du navigateur Web ? Par exemple, apparaîtront-ils dans la source de la page affichée à partir du navigateur Web ?

935voto

Bill the Lizard Points 147311

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. Le fait d'appeler une méthode sur une référence nulle ou d'essayer d'accéder à un champ d'une référence nulle déclenche une exception de type NullPointerException . Ce sont les plus courantes, mais d'autres méthodes sont énumérées dans la section NullPointerException page de javadoc.

C'est probablement l'exemple de code le plus rapide que j'ai pu trouver pour illustrer une NullPointerException serait :

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

Sur la première ligne à l'intérieur main je règle explicitement le paramètre Object référence obj égal à null . Cela signifie que j'ai une référence, mais qu'elle ne pointe vers aucun objet. Ensuite, j'essaie de traiter la référence comme si elle pointait vers un objet en appelant une méthode sur elle. Le résultat est un NullPointerException car il n'y a pas de code à exécuter à l'endroit où pointe la référence.

(C'est un détail technique, mais je pense qu'il faut 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 invalide. Un pointeur nul ne pointe littéralement pas partout ce qui est subtilement différent que de pointer vers un emplacement qui se trouve être invalide).

55 votes

J'ai compris tout ce que vous avez écrit, mais uniquement parce que je code depuis un certain temps et que je sais ce que sont un "pointeur" et une "référence" (et ce qu'est null, d'ailleurs). Lorsque j'essaie de me plonger dans ce genre d'explications, mes étudiants me regardent de travers, parce qu'il n'y a pas assez de contexte.

37 votes

@mmr : Merci pour vos commentaires, vous avez raison. Il est difficile sur internet de vraiment juger où en est quelqu'un, et à quel niveau il est prudent de commencer une explication. Je vais essayer de réviser à nouveau.

27 votes

Une façon plus commune d'obtenir une NullPointerException dans la pratique serait d'oublier d'initialiser explicitement une variable membre à autre chose que null avant de l'utiliser, comme ceci . Avec des variables locales, le compilateur détecterait cette erreur, mais dans ce cas, il ne le fait pas. Peut-être serait-ce un ajout utile à votre réponse ?

732voto

fgb Points 4868

Qu'est-ce qu'une NullPointerException ?

Un bon point de départ est le JavaDocs . Ils s'en occupent :

Lancé lorsqu'une application tente d'utiliser null dans un cas où un est nécessaire. Il s'agit notamment de :

  • Appel de la méthode d'instance d'un objet nul.
  • Accéder ou modifier le champ d'un objet nul.
  • Prendre la longueur de null comme si c'était un tableau.
  • Accéder ou modifier les slots de null comme s'il s'agissait d'un tableau.
  • Lancer null comme si c'était une valeur Throwable.

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

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

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

Comment puis-je le réparer ?

Donc vous avez un NullPointerException . Comment le réparer ? Prenons un exemple simple qui lance un message de type NullPointerException :

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Identifier les valeurs nulles

La première étape consiste à identifier exactement quelles sont les valeurs qui causent l'exception . Pour cela, nous devons faire un peu de 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 levée à la ligne 13 (dans le fichier printString méthode). Regardez la ligne et vérifiez quelles valeurs sont nulles en ajoutant déclarations d'enregistrement ou en utilisant un Débogueur . Nous constatons que s est nulle, et l'appel de la 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.

Retrouver l'origine de ces valeurs

Vérifiez ensuite d'où vient cette valeur. En suivant 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.

Tracer où ces valeurs doivent être définies

Où se trouve this.name ensemble ? Dans le setName(String) . 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 que le paramètre commander que ces méthodes sont appelées, et que la méthode set n'est pas appelée. après la méthode d'impression.

Ceci est suffisant pour nous donner une solution : ajouter un appel à printer.setName() avant d'appeler printer.print() .

Autres corrections

La variable peut avoir un valeur par défaut (et setName peut empêcher qu'il soit défini comme nul) :

private String name = "";

Soit le print o printString méthode peut vérifier la présence de nullité 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 n'arrive toujours pas à trouver le problème

Si vous avez essayé de déboguer le problème et que vous n'avez toujours pas de solution, vous pouvez poster une question pour obtenir plus d'aide, mais assurez-vous d'inclure ce que vous avez essayé jusqu'à présent. Au minimum, inclure la trace de la pile dans la question, et marquer les numéros de ligne importants dans le code. Essayez également de simplifier le code dans un premier temps (cf. SSCCE ).

47 votes

+1 Il est bon d'avoir un exemple qui comprend le suivi de la pile ; il est important de montrer pourquoi sa lecture est importante pour le débogage des NPE. (et c'est pourquoi nous recherchons presque toujours une trace de pile lorsque quelqu'un poste une question sur une erreur).

21 votes

Vous avez mentionné le débogage... Comment cela fonctionne-t-il ? Cela fait un moment que je fais des recherches sur le sujet, mais je ne trouve rien. Je suis sûr qu'un professeur extraordinaire comme vous peut me l'enseigner en une seconde ! Merci beaucoup ! :-)

20 votes

@RuchirBaronia Un débogueur vous permet de parcourir un programme ligne par ligne pour voir quelles méthodes sont appelées et comment les variables sont modifiées. Les IDE devraient disposer d'outils à cet effet. Voir vogella.com/tutoriels/EclipseDebugging/article.html par exemple.

537voto

Stephen C Points 255558

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 ce qui est la façon Java de dire "sans objet".

A NullPointerException est lancée au moment de l'exécution chaque fois que votre programme tente d'utiliser un fichier de type null comme si c'était une vraie 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 "ICI" va tenter d'exécuter les length() sur un null et cela déclenchera un NullPointerException .

Il y a de nombreuses façons d'utiliser un null qui donnera lieu à une NullPointerException . En fait, les seules choses que vous peut faire avec un null sans provoquer un 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 au tableau elle-même ne soit pas nulle !)
  • le passer en tant que paramètre ou le retourner en tant que résultat, ou encore
  • le tester en utilisant le == o != opérateurs, ou instanceof .

Question : Comment lire le suivi de pile 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)
$

Première constatation : la compilation réussit ! Le problème dans le programme n'est PAS une erreur de compilation. Il s'agit d'une temps de fonctionnement erreur. (Certains IDEs peuvent avertir que votre programme lèvera toujours une exception ... mais la norme javac compilateur ne le fait pas).

Deuxième observation : lorsque je lance le programme, il produit deux lignes de "charabia". FAUX ! Ce n'est pas du charabia. C'est un suivi de pile ... et cela fournit information vitale qui vous aidera à retrouver l'erreur dans votre code si vous prenez le temps de le lire attentivement.

Regardons donc 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é déclenchée. Pour un programme simple avec un seul thread (comme celui-ci), ce sera "main". Continuons...
  • Il vous indique le nom complet de l'exception qui a été levée, à savoir java.lang.NullPointerException .
  • Si l'exception est associée à un message d'erreur, celui-ci sera affiché après le nom de l'exception. NullPointerException est inhabituel à cet égard, car il affiche 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 qu'on était dans les main de la méthode Test classe.
  • "Test.java:4" donne le nom du fichier source de la classe, ET il nous indique que l'instruction où cela s'est produit se trouve à 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 "HERE".

Notez que dans un exemple plus compliqué, il y aura beaucoup de lignes dans la trace de la 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 la pile nous indique sans ambiguïté quelle instruction du programme a déclenché le NPE.

Voir aussi : Qu'est-ce qu'une trace de pile, et comment puis-je l'utiliser pour déboguer les erreurs de mon application ?

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

Question : Comment puis-je trouver 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 la pile, le code source et la documentation API pertinente.

Illustrons d'abord avec l'exemple simple (ci-dessus). Nous commençons par examiner la ligne qui, d'après le suivi de la pile, est à l'origine du NPE :

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

Comment cela peut-il déclencher un NPE ?

En fait, il n'y a qu'un seul moyen : cela ne peut se produire que si foo a la valeur null . Nous essayons ensuite d'exécuter le length() méthode sur null et... BANG !

Mais (je vous entends dire) que se passerait-il si le NPE était lancé à l'intérieur de la fonction length() l'appel de la méthode ?

Si c'était le cas, la trace de la pile serait différente. La première ligne "at" indiquerait que l'exception a été levée à une certaine ligne de l'application java.lang.String et la ligne 4 de Test.java serait la deuxième ligne "at".

Alors où est-ce que null venir ? Dans ce cas, c'est évident, et ce que nous devons faire pour y remédier l'est tout autant. (Attribuer une valeur non nulle à foo .)

OK, essayons un exemple un peu plus délicat. Il faudra pour cela 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". La première est pour cette ligne :

return args[pos].length();

et la deuxième est pour cette ligne :

int length = test(foo, 1);

En regardant la première ligne, comment cela peut-il générer un NPE ? Il y a deux façons :

  • Si la valeur de bar est null puis bar[pos] entraînera un NPE.
  • Si la valeur de bar[pos] est null puis en appelant length() sur elle 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 :

Où se trouve bar proviennent-ils ? C'est un paramètre de la test et si nous regardons comment test a été appelé, nous pouvons voir qu'il provient de l' foo variable statique. En outre, 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 changement foo a null ... mais ce n'est pas le cas ici).

Alors qu'en est-il de notre deuxième scénario ? Eh bien, nous pouvons voir que pos est 1 donc cela signifie que foo[1] doit être null . Est-ce possible ?

En effet, c'est le cas ! Et c'est là que réside le problème. Quand on initialise comme ceci :

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

nous attribuons un String[] avec deux éléments qui sont initialisés à null . Après cela, nous n'avons pas modifié le contenu du fichier foo ... donc foo[1] sera toujours null .

Et sur Android ?

Sur Android, la recherche de la cause immédiate d'un NPE est un peu plus simple. Le message d'exception vous indique généralement le type (au moment de la compilation) de la référence nulle que vous utilisez. y la méthode que vous avez essayé d'appeler lorsque le NPE a été lancé. Cela simplifie le processus d'identification de la cause immédiate.

Mais d'un autre côté, Android a des causes communes spécifiques à la plate-forme pour les NPE. Une cause très courante est lorsque getViewById renvoie inopinément un null . Je vous conseille de rechercher des questions-réponses sur la cause de l'imprévu. null valeur de retour.

444voto

smink Points 39640

C'est comme si tu essayais d'accéder à un objet qui est null . Prenons l'exemple suivant :

TypeA objA;

A ce moment-là, vous avez juste déclaré cet 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 est logique.

Voir également l'exemple ci-dessous :

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

4 votes

Si nous donnons System.out.println(a.length()) ; // une exception NullPointerException sera levée, pour éviter cela nous pouvons gérer avec le bloc try catch. merci.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X