Types primitifs et objets wrapper en Java

Utilisation : Les types primitifs sont principalement utilisés pour définir des constantes et des variables locales. Pour les paramètres de méthodes et les attributs d'objets, les types wrapper sont préférés. De plus, seuls les types wrapper peuvent être utilisés avec les généricités.

Stockage : Les variables locales de types primitifs résident dans la pile de la JVM. Les attributs d'instance (non statiques) de types primitifs sont stockés dans le tas. Les types wrapper, étant des objets, sont presque toujours alloués dans le tas.

Occupation mémoire : Les types primitifs consomment significativement moins de mémoire que leurs équivalents wrapper.

Valeurs par défaut : Un attribut de type wrapper non initialisé vaut null, tandis qu'un type primitif a une valeur par défaut (0 pour int, false pour boolean, etc.).

Comparaison : L'opérateur == compare les valeurs pour les types primitifs, mais les références pour les wrappers. Pour comparer les valeurs de wrappers, utilisez obligatoirement equals().

Note sur l'allocation : La JVM HotSpot peut optimiser l'allocation grâce à l'analyse d'évasion. Si un objet n'échappe pas à sa méthode, il peut être alloué sur la pile via substitution scalaire.

Attention : L'affirmation "les types primitifs sont stockés dans la pile" est une simplification excessive. Leur emplacement dépend du contexte : locales dans la pile, attributs d'instance dans le tas.

class ExempleStockage {
    // Attribut d'instance dans le tas
    int valeurInstance = 42;
    
    // Attribut statique dans la métaspace (Java 8+)
    static int valeurClasse = 84;
    
    void methode() {
        // Locale dans la pile
        int locale = 126;
        // static int statiqueLocale = 168; // Erreur de compilation
    }
}

Mécanisme de cache des wrappers

Les classes wrapper implémentent un cache pour optimiser les performances en réutilisant des objets pour des valeurs fréquemment utilisées.

Byte, Short, Integer, Long : cache pour [-128, 127]
Character : cache pour [0, 127]
Boolean : retourne directement les constantes TRUE/FALSE

Implémentation du cache Integer :

public static Integer valueOf(int val) {
    if (val >= IntegerCache.min && val <= IntegerCache.max)
        return IntegerCache.pool[val + (-IntegerCache.min)];
    return new Integer(val);
}

private static class IntegerCache {
    static final int min = -128;
    static final int max;
    static {
        int limit = 127;
        max = limit;
    }
}

Implémentation du cache Character :

public static Character valueOf(char c) {
    if (c <= 127)
        return CharacterCache.pool[c];
    return new Character(c);
}

private static class CharacterCache {
    static final Character pool[] = new Character[128];
    static {
        for (int i = 0; i < pool.length; i++)
            pool[i] = new Character((char)i);
    }
}

Implémentation Boolean :

public static Boolean valueOf(boolean b) {
    return b ? TRUE : FALSE;
}

Les wrappers flottants (Float, Double) n'implémentent pas de cache.

Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true (cache utilisé)

Float x = 3.14f;
Float y = 3.14f;
System.out.println(x == y); // false (pas de cache)

Question piège : que affiche ce code ?

Integer premier = 50;
Integer second = new Integer(50);
System.out.println(premier == second);

Réponse : false. premier utilise le cache, second crée un nouvel objet.

Règle d'or : Comparez toujours les wrappers numériques avec equals().

Autoboxing et unboxing automatique

L'autoboxing convertit automatiquement un primitif en wrapper, l'unboxing fait l'inverse.

Integer nombre = 10;  // autoboxing
int val = nombre;     // unboxing

Bytecode équivalent :

    BIPUSH 10
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    ASTORE 1
    
    ALOAD 1
    INVOKEVIRTUAL java/lang/Integer.intValue ()I
    ISTORE 2

Concrètement :
- Integer nombre = 10 → Integer.valueOf(10)
- int val = nombre → nombre.intValue()

Impact performance : Évitez les conversions inutiles dans les boucles serrées.

// Version lente (crée des objets inutilement)
Long total = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
    total += i; // unboxing + autoboxing à chaque itération
}

// Version optimisée
long totalOptimise = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
    totalOptimise += i;
}

Étiquettes: Java types-primitifs wrapper autoboxing mémoire

Publié le 10 octobre à 01h22