Comment prévenir les NullPointerExceptions en Java
Lucien Chemaly
21 septembre 2023
0 minutes de lectureEn tant que développeur Java expérimenté, vous connaissez probablement les subtilités des exceptions Java. Ces événements signalent des conditions anormales pendant l’exécution d’un programme et, lorsqu’ils ne sont pas correctement gérés, peuvent perturber le fonctionnement normal de votre application et provoquer des plantages ou des comportements inattendus.
Parmi les différentes exceptions Java, vous devez prêter une attention particulière aux NullPointerExceptions. Elles sont particulièrement redoutées, car elles sont une source fréquente d’erreurs d’exécution et peuvent nuire considérablement à la stabilité et à la fiabilité de votre application.
Dans cet article, découvrez tout ce qu’il faut savoir sur les NullPointerExceptions en Java : leurs causes, leurs conséquences et, surtout, les techniques pour les prévenir.
Pourquoi éviter les NullPointerExceptions
Une NullPointerException est une anomalie d’exécution en Java qui se produit lorsqu’une personne tente d’accéder à une référence d’objet contenant la valeur null ou de la modifier. Cette référence null indique que l’objet concerné est absent ou n’a pas été instancié. Lorsque la machine virtuelle Java (JVM) rencontre une référence null lors de la manipulation d’un objet, elle génère une NullPointerException. Comprendre les causes de ces exceptions et y remédier permet de développer des applications plus solides et robustes.
Les NullPointerExceptions peuvent survenir dans différentes situations, notamment les suivantes :
Lorsqu’on accède à une variable d’instance ou qu’on appelle une méthode sur un objet
null.Lorsqu’on accède à un élément d’un tableau ou d’une collection à l’aide d’une référence
null.Lorsqu’on convertit automatiquement une référence
nullissue d’un objet wrapper vers le type primitif correspondant.Lorsqu’on passe une référence
nullen argument à une méthode qui ne la prévoit pas.
Ces situations découlent généralement d’erreurs de programmation : un objet qui n’a pas été initialisé, des valeurs null mal gérées ou l’absence de vérification des valeurs null avant d’effectuer des opérations sur les objets.
Malheureusement, les NullPointerExceptions peuvent avoir de nombreuses conséquences néfastes dans un environnement de développement, notamment :
Plantage de l’application : lorsqu’une
NullPointerExceptionse produit, l’application s’arrête souvent brutalement, ce qui peut mécontenter les utilisateurs et entraîner la perte de données non enregistrées.Comportement imprévisible : les
NullPointerExceptionspeuvent entraîner un comportement imprévisible de l’application, ce qui complique la reproduction et le diagnostic des problèmes.Débogage difficile : trouver la cause profonde d’une
NullPointerExceptionpeut prendre du temps et s’avérer complexe, surtout dans des bases de code volumineuses et complexes.Vulnérabilités de sécurité : dans certains cas, les
NullPointerExceptionspeuvent révéler des vulnérabilités de sécurité, car des utilisateurs malveillants peuvent les exploiter pour contourner certains contrôles ou accéder sans autorisation à des données sensibles.Baisse de la productivité des développeurs : les
NullPointerExceptionfréquentes peuvent nuire à la productivité, car les développeurs doivent consacrer davantage de temps à corriger et à tester ces problèmes.
Heureusement, en comprenant les causes et les conséquences des NullPointerExceptions, ainsi que les techniques pour les prévenir, vous pouvez en réduire la fréquence et créer des applications Java plus stables, plus efficaces et plus sécurisées.
Comment prévenir les NullPointerExceptions en Java
Pour prévenir les NullPointerExceptions en Java, les développeurs peuvent appliquer différentes techniques et bonnes pratiques, notamment utiliser l’opérateur ternaire et la bibliothèque Apache Commons StringUtils.
Utiliser l’opérateur ternaire
Au lieu d’utiliser une instruction if-else, vous pouvez utiliser l’opérateur ternaire pour vérifier les valeurs null et fournir une valeur par défaut, réduisant ainsi le risque de NullPointerExceptions. L’opérateur ternaire permet d’écrire des conditions concises et lisibles qui gèrent les valeurs null et fournissent des valeurs par défaut en Java :
Ce code attribue la valeur default à la variable result si la variable input est null. Si elle n’est pas null, il attribue la valeur de input à result. Il utilise l’opérateur ternaire pour vérifier les valeurs null et fournir une valeur par défaut.
Utiliser StringUtils d’Apache Commons
La bibliothèque Apache Commons StringUtils fournit des méthodes utilitaires pour gérer en toute sécurité les valeurs null dans les chaînes de caractères et réduire le risque de NullPointerException. Elle permet d’éviter les plantages inattendus de l’application et les vulnérabilités de sécurité qui peuvent découler des valeurs null :
Ce code utilise la classe StringUtils du package org.apache.commons.lang3 pour vérifier si une chaîne est null, vide ou composée uniquement d’espaces, puis affecte le résultat à la variable booléenne isBlank.
Vous pouvez réduire les risques de sécurité liés aux bibliothèques tierces comme Apache Commons StringUtils en utilisant Snyk pour détecter les vulnérabilités Java. De plus, il est essentiel d’utiliser la dernière version des bibliothèques tierces pour garantir la sécurité et la stabilité. Pour Apache Commons StringUtils, consultez la dernière version sur son site officiel et mettez à jour la dépendance correspondante dans le fichier pom.xml de votre projet.
Préférer les types primitifs aux objets
Dans la mesure du possible, utilisez des types primitifs plutôt que leurs classes wrapper correspondantes pour éviter les valeurs potentielles null, qui ne peuvent pas être null. Malheureusement, les classes wrapper correspondantes peuvent être null, ce qui peut entraîner une NullPointerException si elles ne sont pas correctement gérées :
Ce code déclare une variable entière primitiveInt de valeur 0, qui est de type primitif (et ne peut donc pas être null), ainsi qu’une variable objectInt de la classe Integer, qui peut être null et entraîner une NullPointerException si elle n’est pas correctement gérée.
Éviter de renvoyer null dans les méthodes
Au lieu de renvoyer des valeurs null depuis vos méthodes, renvoyez un objet vide :
Ce code définit une méthode getStrings() qui renvoie une liste vide de chaînes à l’aide de la méthode Collections.emptyList(), plutôt qu’une valeur null. Cette approche est préférable, car elle évite les NullPointerExceptions potentielles et favorise une gestion cohérente des valeurs de retour, ce qui facilite la lecture et la maintenance du code. Pour éviter de renvoyer null depuis les méthodes Java, une approche intéressante consiste à utiliser Optional. Nous approfondirons ce concept plus loin dans cet article.
Utiliser .equals() en toute sécurité
Appelez .equals() sur un objet dont vous savez qu’il n’est pas null, ou utilisez Objects.equals() pour éviter les NullPointerExceptions lors de la comparaison d’objets :
Ce code déclare deux variables de chaîne : str1 et str2. Il appelle la méthode .equals() sur l’objet non null str1 et utilise Objects.equals() pour comparer str1 et str2 en toute sécurité. Cette méthode vérifie les valeurs null afin d’éviter les NullPointerExceptions potentielles lors de la comparaison d’objets.
Éviter de passer null en argument à une méthode
Utilisez des annotations comme @NotNull pour indiquer qu’une méthode ne doit pas accepter d’arguments null, ce qui permet d’effectuer des vérifications à la compilation :
Utiliser l’annotation @NonNull de Lombok
Lombok est une bibliothèque très utilisée qui simplifie le code Java. L’annotation @NonNull permet d’imposer que les paramètres ne soient pas null, en générant les vérifications null appropriées :
Ce code importe l’annotation @NonNull de la bibliothèque Lombok et l’utilise pour indiquer que le paramètre input de la méthode doSomething ne doit pas être null. Cela permet de prévenir les NullPointerExceptions potentielles en vérifiant les valeurs null à la compilation.
Utiliser le pattern null
Le pattern null est un patron de conception qui fournit un objet par défaut à la place des valeurs null, réduisant ainsi le risque de NullPointerExceptions :
Ce code définit une interface Animal et deux classes qui l’implémentent : Dog et NullAnimal. La classe AnimalFactory contient une méthode, getAnimal(), qui prend un argument String et renvoie une instance de Dog ou de NullAnimal, selon l’argument.
Cette approche est utile pour gérer les valeurs null, car elle permet de créer un objet NullAnimal par défaut lorsqu’un argument invalide ou null est transmis à getAnimal(). Cela peut prévenir les NullPointerExceptions potentielles et garantir un comportement cohérent du programme.
Utiliser l’API Stream de Java pour gérer les valeurs null dans les collections
L’API Stream de Java fournit des méthodes permettant de gérer en toute sécurité les valeurs null dans les collections :
Ce code utilise un flux pour filtrer les éléments null d’une liste de chaînes et ainsi prévenir les NullPointerExceptions potentielles qui peuvent survenir lorsqu’on tente d’accéder à des valeurs null dans la liste.
Cette approche simplifie la vérification des valeurs null et aide à prévenir les plantages inattendus de l’application et les vulnérabilités de sécurité qui peuvent découler des valeurs null dans les collections.
Valider les arguments d’une méthode à l’aide d’une méthode utilitaire dédiée
Créez une méthode utilitaire pour valider les arguments et lever une exception personnalisée lorsque des valeurs null sont rencontrées. Cela peut aider à imposer que les paramètres des méthodes ne soient pas null :
Ce code définit une méthode checkNotNull qui lève une IllegalArgumentException si le paramètre reference est null, ainsi qu’une méthode doSomething qui utilise checkNotNull pour s’assurer que le paramètre input n’est pas null avant de poursuivre l’implémentation de la méthode.
Cette approche est importante pour prévenir les NullPointerExceptions potentielles : elle vérifie les valeurs null à l’exécution et lève une exception si des valeurs null sont rencontrées.
Utiliser des méthodes par défaut dans les interfaces
Lorsque vous utilisez des interfaces, fournissez des méthodes par défaut pour gérer les objets potentiellement null et éviter ainsi les NullPointerExceptions :
Ce code définit une interface Processor dotée d’une méthode par défaut process(), qui vérifie si les valeurs d’entrée sont null et appelle soit doProcess(), soit handleNullInput(). La classe PMyProcessor implémente l’interface Processor et redéfinit les méthodes doProcess() et handleNullInput() pour définir un comportement personnalisé.
Cette approche aide à prévenir les NullPointerExceptions potentielles en fournissant une méthode cohérente pour gérer les valeurs d’entrée null et en permettant de mettre en place un comportement personnalisé ou une gestion des exceptions lorsque de telles valeurs null sont rencontrées.
Utiliser le pattern Builder
Le pattern Builder peut vous aider à créer des objets complexes avec des paramètres facultatifs (comme la couleur, la taille, la forme ou d’autres caractéristiques personnalisables), tout en garantissant que les paramètres obligatoires sont toujours définis et non null :
Ce code définit une classe Person dotée d’un constructeur privé qui n’accepte qu’un objet Builder, lequel contient deux champs : firstName et lastName. La classe Builder garantit que le champ firstName n’est pas null, en le vérifiant lors de la construction à l’aide de Objects.requireNonNull(). Le champ lastName peut, lui, être null, mais doit être vérifié et défini à l’aide de la méthode lastName().
Cette approche aide à prévenir les NullPointerExceptions potentielles en garantissant que tous les champs obligatoires sont initialisés et que les valeurs null sont vérifiées lors de la construction. Elle favorise ainsi la cohérence et la fiabilité du programme.
Utiliser le verrouillage à double vérification avec le pattern singleton
Lors de la création de singletons, le verrouillage à double vérification peut contribuer à garantir que l’instance singleton n’est jamais null :
Ce code définit une classe Singleton qui garantit qu’une seule instance de la classe est créée à l’exécution et utilise le verrouillage à double vérification pour éviter les éventuelles conditions de concurrence et les NullPointerExceptions.
Cette approche est importante pour éviter les NullPointerExceptions, car elle garantit que l’instance de la classe Singleton n’est créée qu’une seule fois et que les appels ultérieurs à la méthode getInstance() renvoient la même instance. Cela permet d’éviter que des valeurs null soient accidentellement affectées à la variable instance et provoquent d’éventuelles NullPointerExceptions.
Utiliser la classe Optional
La classe Optional est un outil puissant pour éviter les NullPointerExceptions en gérant explicitement les valeurs potentiellement null :
Ce code utilise la classe Optional pour gérer les valeurs null, en indiquant explicitement qu’une valeur peut être absente et en utilisant orElse() pour fournir une valeur par défaut, ce qui évite d’éventuelles NullPointerExceptions. Il est important de ne pas appeler directement get() sur Optional. Utilisez plutôt des méthodes comme orElse() pour fournir une valeur par défaut lorsque l’objet Optional est vide, évitant ainsi une NoSuchElementException.
Cette approche indique clairement qu’une valeur peut être présente ou non et impose également de gérer correctement ces cas, ce qui contribue à éviter des erreurs potentielles telles qu’une NullPointerException ou une NoSuchElementException.
Tester souvent et tôt
Veillez à écrire des tests unitaires complets afin de détecter les NullPointerExceptions pendant le développement, avant qu’elles ne posent problème en production :
Ce code définit un test JUnit pour la méthode getStrings() de la classe MyClass. Il crée ensuite une instance de MyClass, appelle la méthode getStrings() et vérifie que le résultat n’est pas null à l’aide de la méthode assertNotNull().
Cette approche permet d’éviter les éventuelles NullPointerExceptions en vérifiant que le résultat de la méthode getStrings() est not null avant d’effectuer d’autres opérations. En s’assurant que le résultat est not null, le test peut aider à détecter tout problème potentiel lié aux valeurs null et à prévenir les plantages inattendus de l’application ou les vulnérabilités de sécurité.
Conclusion sur NullPoint
Éviter les NullPointerExceptions dans les applications Java est essentiel pour créer des logiciels robustes, fiables et sécurisés. Depuis Java 14, des améliorations facilitent la gestion des NullPointerExceptions, ainsi que leur prévention et leur diagnostic. Il peut donc être avantageux de passer à une version plus récente de Java pour faciliter le débogage.
En combinant différentes techniques, comme l’utilisation de l’opérateur ternaire, la validation des arguments de méthode et l’adoption de modèles de conception tels que Builder et le pattern objet nul, vous pouvez réduire efficacement le risque de rencontrer des NullPointerExceptions. Ces pratiques contribuent non seulement à améliorer la stabilité de l’application, mais aussi l’expérience globale de développement.
Snyk est un outil essentiel pour les développeurs qui souhaitent créer des applications sécurisées et fiables. Avec Snyk, vous avez accès à un éventail d’outils et de services conçus pour détecter et corriger les vulnérabilités de sécurité dans votre code. Snyk s’intègre également aux environnements et plateformes de développement les plus courants, ce qui facilite l’intégration de la sécurité à votre processus de développement.
Pour faire passer votre expérience de développement Java au niveau supérieur et contribuer à la sécurité et à la stabilité de l’écosystème logiciel, pensez à utiliser certains des outils puissants de Snyk, notamment les plugins IDE, le CLI et les cours pratiques gratuits sur la sécurité Java. Ces outils vous aident à détecter et à signaler les vulnérabilités de sécurité, y compris les NullPointerExceptions, afin de garantir la sécurité et la fiabilité de vos applications.
Détectez et corrigez les vulnérabilités Java
Sécurisez vos applications grâce à l’analyse des vulnérabilités de Snyk et à ses recommandations de correction.
Aucune carte bancaire requise.
Ou inscrivez-vous avec Azure AD Docker ID Bitbucket
En utilisant Snyk, vous acceptez de respecter nos politiques, notamment nos Conditions d’utilisation et notre Politique de confidentialité.



