Qu'est-ce que null en Java ?

null est une valeur spéciale en Java qui représente l'absence de référence. Elle peut être affectée à n'importe quelle variable de type objet, mais jamais à un type primitif. Bien manipulée, elle exprime l'absence de données ; mal manipulée, elle produit la fameuse NullPointerException.

Définition formelle

En Java, toutes les variables d'un type non primitif (objets, tableaux, interfaces) contiennent soit :

  • une référence vers un objet existant dans le tas ;
  • la valeur spéciale null, qui signifie « cette variable ne pointe vers rien ».
String nom = null;          // aucune chaîne n'est référencée
User user = null;           // aucun utilisateur référencé
int[] scores = null;        // aucun tableau référencé
int age = null;             // ❌ Erreur : primitif ne peut être null

null n'est pas 0, pas la chaîne vide

ValeurTypeSignification
nullRéférenceAucun objet
""StringChaîne existante, vide
0intEntier valant zéro
falsebooleanBooléen faux
[] (tableau vide)ArrayTableau existant, de taille 0

Une chaîne null n'est pas vide — elle n'existe pas.

NullPointerException

L'erreur survient dès que vous tentez d'utiliser une référence nulle comme un objet :

String nom = null;
int longueur = nom.length(); // ❌ NullPointerException — nom ne pointe sur rien

Depuis Java 14, le message de la NPE pointe précisément quel champ était null (helpful NullPointerException) :

Cannot invoke "String.length()" because "nom" is null

Activé par défaut dans Java 15+.

Tester null correctement

if (nom != null) {
    System.out.println(nom.length());
}

// Ou utiliser Objects
if (Objects.isNull(nom)) { ... }
if (Objects.nonNull(nom)) { ... }

Ne jamais utiliser equals() sur une variable qui peut être null :

if (nom.equals("Alice")) { ... } // ❌ NPE si nom == null

// ✅ Inverser l'ordre : la constante ne peut pas être null
if ("Alice".equals(nom)) { ... }

// ✅ Ou utiliser Objects.equals
if (Objects.equals(nom, "Alice")) { ... }

Optional : exprimer l'absence sans null

Depuis Java 8, Optional<T> est une alternative typée. Une méthode qui peut ne pas retourner de valeur le déclare explicitement :

import java.util.Optional;

public Optional<User> findById(long id) {
    User u = repository.get(id);
    return Optional.ofNullable(u);
}

// Utilisation
findById(42)
    .map(User::getName)
    .ifPresent(System.out::println);

String nom = findById(42)
    .map(User::getName)
    .orElse("Inconnu");

Règles d'usage :

  • Utilisez Optional pour les valeurs de retour qui peuvent être absentes.
  • Ne l'utilisez pas comme champ d'entité, ni comme paramètre.
  • Ne le retournez jamais null : si la méthode renvoie un Optional, renvoyez Optional.empty().

Annotations @Nullable et @NonNull

Plusieurs bibliothèques (JetBrains, JSR-305, Checker Framework) proposent des annotations que les IDE et outils statiques vérifient :

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public @Nullable String getNom() { return nom; }
public void setEmail(@NotNull String email) {
    if (email == null) throw new IllegalArgumentException();
}

IntelliJ vous alerte dès qu'une valeur @Nullable est utilisée sans vérification, ou qu'une variable @NotNull reçoit un null.

Valeurs par défaut pour les champs

Les champs d'objet reçoivent automatiquement des valeurs par défaut :

  • Référence (objet, tableau) → null
  • Entier (byte/short/int/long) → 0
  • Flottant (float/double) → 0.0
  • Booléen → false
  • char → '\u0000'

Les variables locales, en revanche, n'ont pas de valeur par défaut — il faut les initialiser avant usage, sous peine d'erreur de compilation.

null dans les collections

La plupart des collections acceptent null (ArrayList, HashMap, HashSet) mais pas toutes : Map.of(), List.of() et les concurrent collections rejettent null.

List<String> liste = new ArrayList<>();
liste.add(null); // ✅ autorisé

List<String> immut = List.of("a", null); // ❌ NullPointerException

Bonnes pratiques

  1. Éviter de retourner null : préférez une collection vide (List.of()), un Optional, ou lancer une exception.
  2. Valider en entrée : utilisez Objects.requireNonNull(param, "nom") au début des constructeurs et setters.
  3. Annotations @Nullable / @NonNull : documentent l'intention et sont vérifiées par les IDE.
  4. Initialiser les champs d'instance plutôt que de compter sur le null implicite.
  5. Préférer Objects.equals aux appels directs de equals.

En résumé, null est un outil utile mais glissant. La combinaison Optional + annotations + validation précoce réduit drastiquement les NullPointerException dans un code moderne.