Bonjour ! Dans la leçon précédente, nous avons mis en place une structure de projet professionnelle avec Maven. Vous êtes maintenant prêt à écrire du code Java dans un environnement organisé, capable de gérer des dépendances et de suivre des standards de l'industrie.
Aujourd'hui, nous plongeons au cœur du langage Java pour comprendre comment il manipule les données. L'objectif est d'apprendre à distinguer les types primitifs des types référence, un concept fondamental qui a des implications sur la performance, l'utilisation de la mémoire et la manière d'écrire votre code. Nous verrons également comment les classes wrapper (ou classes enveloppes) font le pont entre ces deux mondes. Votre expérience avec JavaScript, qui possède une distinction similaire, vous donnera une bonne base de départ.
1. La grande distinction : Primitifs vs. Référence
En Java, il existe deux grandes familles de types de données. Comprendre leur différence est crucial.
Les Types Primitifs
Un type primitif est le type de donnée le plus simple. Une variable de type primitif contient directement la valeur elle-même. C'est comme une petite boîte qui contient un nombre, un caractère ou une valeur vrai/faux.
Java en compte huit :
- Types entiers :
byte,short,int,long - Types à virgule flottante :
float,double - Type caractère :
char - Type booléen :
boolean
Les types primitifs — Le langage Java
Cet article du site gayerie.dev offre une excellente introduction aux types primitifs en Java.
Lisez les sections intitulées : "Les types primitifs" : pour voir la liste complète. "Le type booléen : boolean", "Le type caractère : char", "Les types entiers" et "Les types à virgule flottante". Focalisez-vous sur le tableau "Taille mémoire" qui vous donne une idée de l'espace occupé par chaque type. Notez aussi la convention de nommage : les types primitifs s'écrivent toujours en minuscules.
Voici comment on les utilise :
int age = 30; // La variable 'age' contient la valeur 30
double prix = 19.99; // La variable 'prix' contient la valeur 19.99
boolean estActif = true; // La variable 'estActif' contient la valeur true
char initiale = 'A'; // La variable 'initiale' contient la valeur 'A'
La caractéristique la plus importante des types primitifs est qu'ils ne sont pas des objets. Cela signifie que vous ne pouvez pas appeler de méthodes sur eux. Le code age.toString() provoquerait une erreur de compilation.
Les Types Référence
À l'opposé, une variable de type référence ne contient pas l'objet lui-même, mais une référence (ou une adresse mémoire) qui pointe vers l'emplacement de l'objet. C'est comme une télécommande qui pointe vers la télévision, ou une adresse postale qui indique où se trouve une maison.
Toutes les classes que vous créerez, ainsi que les classes intégrées comme String, ArrayList ou Object, sont des types référence.
La vidéo suivante explique très bien ce concept de "pointeur" vers un emplacement en mémoire.
Apprendre Java #2 Variable et Référence
La vidéo "Apprendre Java #2 Variable et Référence" de la chaîne JirAWS utilise une excellente analogie avec les "cases" de la mémoire RAM pour visualiser ce qu'est une référence.
Regardez ces trois passages clés : L'explication de la RAM : Visualisez comment un objet est stocké dans une "case" mémoire et comment la variable n'est qu'un pointeur vers cette case. L'unicité des objets : Comprenez que même deux objets identiques sont des instances distinctes dans deux cases mémoire différentes. Références partagées : Voyez ce qui se passe lorsque deux variables pointent vers le même objet. Une modification via une variable est visible via l'autre.
Voici un exemple en code :
// 'nomUtilisateur' est une référence qui pointe vers un objet String
String nomUtilisateur = new String("Alice");
// 'autreNom' est une autre référence
// Elle ne crée pas un nouvel objet, elle pointe vers le MÊME objet que 'nomUtilisateur'
String autreNom = nomUtilisateur;
Contrairement aux primitifs, vous pouvez appeler des méthodes sur les types référence, car ils représentent de véritables objets (nomUtilisateur.toUpperCase()). De plus, une variable de type référence peut avoir la valeur spéciale null, signifiant qu'elle ne pointe vers aucun objet.
2. Les Classes Wrapper : Le Pont entre Deux Mondes
Alors, que se passe-t-il si nous avons besoin de traiter un type primitif comme un objet ? Par exemple, pour le stocker dans une collection qui n'accepte que des objets (nous verrons cela bientôt).
C'est là qu'interviennent les classes wrapper (ou classes enveloppes). Chaque type primitif a une classe correspondante qui "enveloppe" la valeur primitive dans un objet.
| Type Primitif | Classe Wrapper |
|---|---|
int |
Integer |
double |
Double |
char |
Character |
boolean |
Boolean |
long |
Long |
short |
Short |
byte |
Byte |
float |
Float |
Notez la convention : les classes wrapper commencent par une majuscule.
Cette vidéo explique très clairement leur utilité.
PROGRAMMER EN JAVA (Débutant) - 57 - Les Wrappers
Cette vidéo de la chaîne "LES TEACHERS DU NET" intitulée "Les Wrappers" est une excellente introduction.
Regardez les sections suivantes : Qu'est-ce qu'un wrapper ? : Il présente la correspondance entre types primitifs et wrappers. Avantages des wrappers : Il démontre le principal avantage : la possibilité d'utiliser des méthodes sur ces objets. Exemple pratique : L'exemple avec Integer.parseInt() est très pertinent. En tant que développeur web, vous avez constamment besoin de convertir des entrées (qui sont des chaînes de caractères) en nombres.
Utilités des classes wrapper :
- Méthodes utilitaires : Elles fournissent des méthodes très pratiques. La plus courante est la conversion d'une chaîne de caractères en un type numérique.
String input = "123"; int nombre = Integer.parseInt(input); // Convertit la chaîne "123" en l'entier 123 - Utilisation dans les collections : Comme nous le verrons dans le prochain module, les collections standards de Java comme
ArrayListne peuvent stocker que des objets. On ne peut pas faireArrayList<int>, mais on peut faireArrayList<Integer>. - Représenter l'absence de valeur : Un
Integerpeut êtrenull, alors qu'unintdoit toujours avoir une valeur (par défaut, 0). C'est très utile pour représenter une donnée qui n'a pas encore été fournie, par exemple dans une base de données.
3. Autoboxing et Unboxing : La Magie du Compilateur
Pour faciliter la vie du développeur, Java effectue des conversions automatiques entre les types primitifs et leurs wrappers.
- Autoboxing : Conversion automatique d'un primitif vers son wrapper.
Integer nombreWrapper = 100; // Le compilateur traduit ça en : Integer nombreWrapper = Integer.valueOf(100); - Unboxing : Conversion automatique d'un wrapper vers son primitif.
int nombrePrimitif = nombreWrapper; // Le compilateur traduit ça en : int nombrePrimitif = nombreWrapper.intValue();
Les types primitifs — Le langage Java
Retournez à l'article de gayerie.dev pour lire la section finale sur ce mécanisme.
Lisez la section "L’autoboxing". Elle explique ce mécanisme avec des exemples clairs et, surtout, elle met en évidence un piège très important à connaître.
⚠️ Attention, bonne pratique professionnelle :
L'autoboxing est pratique, mais dangereux si la variable wrapper est null. Tenter de faire l'unboxing d'une référence null provoquera une erreur à l'exécution : une NullPointerException.
Integer monNombre = null;
int nombre = monNombre; // CRASH ! Provoque une NullPointerException à l'exécution.
C'est l'une des erreurs les plus courantes en Java. Soyez donc toujours prudent lorsque vous manipulez des wrappers qui pourraient être null.
4. Exercice Pratique
Mettons ces concepts en application dans notre projet Maven.
- Ouvrez votre projet dans IntelliJ IDEA.
- Dans le dossier
src/main/java, créez un package (clic droit surjava-> New -> Package). Nommez-le avec votre groupId inversé (par exemple,fr.votreentreprise.fundamentals). - Dans ce nouveau package, créez une classe Java nommée
TypesDemo. - Copiez-collez le code suivant dans cette classe et suivez les instructions en commentaires.
package fr.votreentreprise.fundamentals; // Adaptez avec votre nom de package
public class TypesDemo {
public static void main(String[] args) {
// --- Partie 1: Primitifs vs. Référence ---
int a = 10;
Integer b = 10; // Autoboxing
System.out.println("Type primitif int: " + a);
System.out.println("Type référence Integer: " + b);
// Essayez de décommenter la ligne suivante. Que se passe-t-il ?
// a.toString(); // -> Erreur de compilation : "int cannot be dereferenced"
// Ceci fonctionne car 'b' est un objet avec des méthodes.
System.out.println("Conversion de l'Integer en String: " + b.toString());
// --- Partie 2: Méthodes utilitaires ---
String userInput = "25";
int age = Integer.parseInt(userInput);
System.out.println("L'année prochaine, vous aurez: " + (age + 1));
// --- Partie 3: Le piège du NullPointerException ---
Integer valeurNulle = null;
// Que se passe-t-il si on essaie d'affecter un Integer null à un int ?
try {
int valeurPrimitive = valeurNulle; // Cette ligne va lancer une exception
System.out.println("Cette ligne ne sera jamais affichée.");
} catch (NullPointerException e) {
System.out.println("Erreur attrapée ! On ne peut pas 'unboxer' une valeur nulle.");
System.out.println("Message de l'exception: " + e.getMessage());
}
System.out.println("Le programme continue après l'erreur gérée.");
}
}
Exécutez ce code (clic droit sur TypesDemo -> Run 'TypesDemo.main()'). Observez les sorties dans la console et assurez-vous de bien comprendre chaque partie.
Conclusion
Félicitations ! Vous avez exploré une des distinctions les plus fondamentales de Java.
Ce qu'il faut retenir :
- Types primitifs (
int,double, etc.) : stockent directement la valeur, sont rapides, en minuscules, et n'ont pas de méthodes. - Types référence (
String,Integer, etc.) : stockent une adresse vers un objet, en majuscules, peuvent êtrenullet possèdent des méthodes. - Les classes wrapper (
Integer,Double, etc.) servent de pont en encapsulant une valeur primitive dans un objet. Elles sont indispensables pour les collections et offrent des méthodes utilitaires. - L'autoboxing/unboxing facilite la conversion, mais il faut se méfier de la
NullPointerExceptionlors de l'unboxing d'une référencenull.
Dans la prochaine leçon, nous allons nous concentrer sur un type référence que vous utiliserez constamment : la classe String. Nous verrons en détail comment manipuler des chaînes de caractères, une compétence essentielle pour tout développeur backend.