Au chapitre 1, vous avez écrit du code dans une classe main.
Maintenant, vous allez concevoir vos propres types : une classe décrit un modèle,
un objet est une instance concrète de ce modèle.
L’objectif : savoir créer une classe, l’encapsuler, la relier à d’autres objets,
et comparer correctement vos instances.
- Classe vs objet (analogie, conception)
- Attributs, méthodes,
new, référence - Constructeurs, surcharge,
this/this(...) - Encapsulation (
private, default,public, getters / setters) static,static final, instance vs classe- Références,
null,NullPointerException - Association et composition
toString,equals,hashCode(principe)enumavec attributs et méthodes
2.1Classe et objet
Concevoir une classe et un objet
Avant d’ouvrir l’éditeur, posez-vous une question simple : qu’est-ce qu’une classe, et qu’est-ce qu’un objet ? Pour l’instant, pas de syntaxe Java : seulement des idées tirées de la vie réelle.
Étape 1 — Le plan et la réalisation
Imaginez le plan d’un architecte pour une maison. Le plan décrit ce que toute maison de ce type doit avoir : des pièces, des portes, des fenêtres. Mais le plan n’est pas la maison.
Quand on construit une maison à Casablanca, puis une autre à Rabat, on obtient deux maisons concrètes. Elles suivent le même plan, pourtant chacune a sa propre adresse, sa propre couleur, ses propres habitants.
En programmation orientée objet, on retrouve exactement cette distinction :
- la classe, c’est le plan (le modèle) ;
- l’objet, c’est une réalisation concrète de ce plan.
Étape 2 — Analogie du véhicule
Dans la vie quotidienne, le mot « véhicule » désigne une catégorie. Vous savez qu’un véhicule a en général une marque, une immatriculation, une vitesse, et qu’il peut démarrer ou s’arrêter. À ce stade, vous ne parlez encore d’aucune voiture précise.
Les objets, ce sont les véhicules concrets : la voiture de Youssef Alaoui, le taxi de Fatima Benjelloun, un bus à Casablanca… Tous appartiennent à la même catégorie « véhicule », mais chacun a ses propres caractéristiques.
Étape 3 — Analogie de la personne
Même raisonnement avec les personnes. L’idée de « personne » regroupe ce qui est commun : un nom, un prénom, un âge… Ce n’est pas encore Amine Tazi, ni Sara Idrissi.
Les objets, ce sont les personnes concrètes de votre application : Amine Tazi (22 ans), Sara Idrissi (20 ans), Omar Chakir (28 ans)… Chacune est un exemplaire du même modèle « personne », avec ses propres valeurs.
Étape 4 — Passer à Java
Maintenant que l’idée est claire, vous allez l’écrire en Java. Dans la suite du chapitre, nous prendrons surtout le modèle Personne (nom, prénom, âge), avec des exemples concrets. Commencez par déclarer la classe : c’est l’équivalent du plan.
2.1.1 – Déclaration d’une classe
En Java, vous déclarez une classe avec le mot-clé class.
C’est le moment où le modèle devient du code.
public class Personne {
// contenu de la classe
}
public se place dans un fichier
du mĂŞme nom (Personne.java), en PascalCase.
2.1.2 – Attributs et méthodes
À l’intérieur de la classe, vous placez deux familles d’éléments :
- les attributs : les données de chaque objet (nom, prénom, âge…) ;
- les méthodes : les actions que l’objet peut réaliser (afficher, vieillir…).
public class Personne {
String nom;
String prenom;
int age;
void afficher() {
System.out.println(prenom + " " + nom + ", " + age + " ans");
}
}
Pour l’instant, les attributs sont encore visibles de l’extérieur.
Nous les rendrons private un peu plus loin (encapsulation).
2.1.3 – Instanciation avec new
Une instance est un objet concret créé à partir d’une classe. En d’autres termes : la classe est le modèle ; l’instance est l’exemplaire réel obtenu à partir de ce modèle. On dit aussi « créer une instance » pour dire « créer un objet ».
La classe seule ne crée aucune personne. Pour obtenir une instance, vous utilisez
new : Java réserve de la mémoire et construit l’objet.
Cette opération s’appelle l’instanciation.
public class MainClass {
public static void main(String[] args) {
Personne p = new Personne();
p.nom = "Tazi";
p.prenom = "Amine";
p.age = 22;
p.afficher();
}
}
p est une instance de la classe Personne
(ici : Amine Tazi, 22 ans).
new Personne() réalise l’instanciation.
new Personne() crée l’objet ;
la variable p contient une référence vers cet objet.
2.1.4 – Référence et objet
Ne confondez pas la variable et l’objet. La variable stocke l’adresse (la référence). L’objet, lui, vit en mémoire.
public class MainClass {
public static void main(String[] args) {
Personne a = new Personne();
Personne b = a; // b pointe vers LE MĂŠME objet que a
b.prenom = "Sara";
b.nom = "Idrissi";
System.out.println(a.prenom); // Sara
}
}
Ici, vous n’avez qu’un seul objet, et deux variables qui le désignent. Nous reviendrons sur ce point en 2.6.
2.2Attributs et méthodes
2.2.1 – Attributs d’instance
Un attribut d’instance appartient à chaque objet. Deux personnes peuvent avoir des noms différents, car chaque instance a sa propre copie des attributs.
public class MainClass {
public static void main(String[] args) {
Personne a = new Personne();
Personne b = new Personne();
a.prenom = "Youssef";
a.nom = "Alaoui";
b.prenom = "Fatima";
b.nom = "Benjelloun";
// a et b sont indépendants
}
}
Rappel du chapitre 1 : un attribut reçoit une valeur par défaut
(0, false, null…) s’il n’est pas initialisé.
2.2.2 – Méthodes d’instance
Une méthode d’instance s’appelle sur un objet : a.afficher().
À l’intérieur de la méthode, vous accédez directement aux attributs de cet objet.
public class Personne {
String nom;
String prenom;
int age;
void afficher() {
System.out.println(prenom + " " + nom + ", " + age + " ans");
}
}
2.2.3 – Paramètres et valeurs de retour
Une méthode peut recevoir des paramètres et renvoyer une valeur avec return.
Si elle ne renvoie rien, le type de retour est void.
public class Personne {
String nom;
String prenom;
int age;
boolean estMajeur() {
return age >= 18;
}
void vieillir(int annees) {
age = age + annees;
}
}
2.2.4 – Passage des arguments : par valeur et par adresse
Quand vous appelez une méthode, Java transmet les arguments au paramètre. Selon le type, on parle de passage par valeur ou de passage par adresse.
-
Passage par valeur : pour un primitif, vous copiez la valeur
(
int,double…). La méthode travaille sur une copie : modifier le paramètre ne change pas la variable d’origine chez l’appelant. - Passage par adresse : pour un objet, vous copiez la référence (l’adresse), pas l’objet entier. La méthode reçoit donc l’adresse du même objet : elle peut modifier son contenu (ses attributs).
public class MainClass {
// Passage par valeur : la copie locale change, pas la variable d’origine
static void essayerDeChanger(int age) {
age = 30;
}
// Passage par adresse : on modifie l’objet via son adresse
static void changerAge(Personne p) {
p.age = 30;
}
public static void main(String[] args) {
int a = 22;
essayerDeChanger(a);
System.out.println(a); // 22
Personne amine = new Personne();
amine.prenom = "Amine";
amine.nom = "Tazi";
amine.age = 22;
changerAge(amine);
System.out.println(amine.age); // 30
}
}
2.2.5 – this
Le mot-clé this désigne l’objet courant :
celui sur lequel vous avez appelé la méthode.
Il sert surtout à lever une ambiguïté quand un paramètre porte le même nom qu’un attribut.
Sans this, le nom age désigne le paramètre, pas l’attribut.
L’affectation age = age ne changerait donc rien à l’objet.
public class Personne {
String nom;
String prenom;
int age;
void setAge(int age) {
this.age = age; // this.age = attribut de l’objet ; age = paramètre
}
void afficher() {
System.out.println(prenom + " " + nom + ", " + age + " ans");
}
}
public class MainClass {
public static void main(String[] args) {
Personne p = new Personne();
p.nom = "Tazi";
p.prenom = "Amine";
p.setAge(22); // this désigne l’objet p
p.afficher(); // Amine Tazi, 22 ans
}
}
p.setAge(22), à l’intérieur de
setAge, this représente p.
Donc this.age = age signifie : « range 22 dans l’attribut
age de cet objet ».
2.3Constructeurs
2.3.1 – Rôle du constructeur
Un constructeur initialise un objet au moment du new.
Il porte le même nom que la classe et n’a pas de type de retour
(pas mĂŞme void).
public class Personne {
private String nom;
private String prenom;
private int age;
public Personne(String nom, String prenom, int age) {
this.nom = nom;
this.prenom = prenom;
this.age = age;
}
}
public class MainClass {
public static void main(String[] args) {
Personne p = new Personne("Alaoui", "Youssef", 25);
}
}
2.3.2 – Constructeur par défaut
Tant que vous n’avez écrit aucun constructeur, ceci fonctionne :
public class Personne {
String nom;
String prenom;
int age;
// aucun constructeur écrit → Java fournit Personne()
}
public class MainClass {
public static void main(String[] args) {
Personne p = new Personne(); // OK : constructeur par défaut
p.nom = "Tazi";
p.prenom = "Amine";
p.age = 22;
}
}
Dès que vous ajoutez un constructeur avec paramètres, le constructeur par défaut
disparaît. new Personne() ne compile plus, sauf si vous l’écrivez vous-même :
public class Personne {
String nom;
String prenom;
int age;
public Personne(String nom, String prenom, int age) {
this.nom = nom;
this.prenom = prenom;
this.age = age;
}
}
public class MainClass {
public static void main(String[] args) {
// Personne a = new Personne(); // ❌ erreur : pas de constructeur sans paramètre
Personne b = new Personne("Alaoui", "Youssef", 25); // OK
}
}
2.3.3 – Surcharge de constructeurs
Vous pouvez proposer plusieurs constructeurs, avec des listes de paramètres différentes.
C’est la surcharge : Java choisit le bon selon les arguments du new.
public class Personne {
private String nom;
private String prenom;
private int age;
public Personne(String nom, String prenom) {
this.nom = nom;
this.prenom = prenom;
this.age = 0;
}
public Personne(String nom, String prenom, int age) {
this.nom = nom;
this.prenom = prenom;
this.age = age;
}
}
2.3.4 – this(...)
Vous avez parfois plusieurs constructeurs qui font presque le mĂŞme travail.
Pour ne pas répéter le même code, un constructeur peut appeler un autre constructeur
grâce à this(...).
public class Personne {
private String nom;
private String prenom;
private int age;
public Personne(String nom, String prenom) {
this(nom, prenom, 0); // OK : première instruction
}
public Personne(String nom, String prenom, int age) {
this.nom = nom;
this.prenom = prenom;
this.age = age;
}
// ❌ erreur : this(...) n’est pas en première position
// public Personne(String nom) {
// this.nom = nom;
// this(nom, "Inconnu", 0);
// }
}
2.4Encapsulation
Jusqu’ici, n’importe qui pouvait écrire directement p.age = -5.
Ce n’est pas très sûr, n’est-ce pas ?
L’encapsulation répond à ce problème : vous protégez les données de l’objet
et vous contrôlez la façon dont on les lit ou on les modifie.
2.4.1 – private, default et public
L’idée est simple. Vous cachez l’intérieur de l’objet, et vous n’ouvrez que ce qui est utile à l’extérieur. Pour cela, Java propose plusieurs niveaux d’accès. Pour l’instant, retenez-en trois :
-
private: accessible uniquement dans la classe elle-même ; - default (aucun mot-clé) : accessible dans le même package, mais pas depuis un autre package ;
-
public: accessible depuis n’importe où.
Il existe aussi protected : nous le verrons au chapitre sur l’héritage.
En pratique, vous rendez souvent les attributs private.
Ainsi, personne ne peut les modifier directement depuis l’extérieur.
public class Personne {
private String nom; // private : seulement dans Personne
private String prenom;
private int age;
String codeInterne; // default : visible dans le mĂŞme package
public Personne(String nom, String prenom, int age) {
this.nom = nom;
this.prenom = prenom;
this.age = age;
}
}
public class MainClass {
public static void main(String[] args) {
Personne p = new Personne("Tazi", "Amine", 22);
// p.age = -5; // ❌ erreur : age est private
}
}
2.4.2 – Getters et setters
Si les attributs sont privés, comment les lire ou les modifier ? Vous passez par des méthodes publiques : les getters et les setters.
Un getter renvoie la valeur d’un attribut. Un setter la change — et c’est le bon endroit pour vérifier que la valeur est correcte.
public class Personne {
private String nom;
private String prenom;
private int age;
public Personne(String nom, String prenom, int age) {
this.nom = nom;
this.prenom = prenom;
this.age = age;
}
public String getNom() {
return nom;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age < 0) {
System.err.println("Âge négatif");
return;
}
this.age = age;
}
}
public class MainClass {
public static void main(String[] args) {
Personne p = new Personne("Tazi", "Amine", 22);
System.out.println(p.getAge()); // 22
p.setAge(23); // OK
p.setAge(-1); // message d’erreur, age inchangé
}
}
nom, vous écrivez getNom() et
setNom(...).
Pour un booléen, on préfère souvent is au lieu de get :
par exemple, si l’attribut s’appelle actif, la méthode de lecture
s’écrit isActif().
2.4.3 – Protection de l’état interne d’un objet
Pourquoi tout cela ? Pour protéger l’état de l’objet. Sans encapsulation, un autre programmeur (ou vous-même plus tard) pourrait laisser un âge négatif, un nom vide, ou toute autre valeur absurde.
Avec l’encapsulation, les règles vivent dans la classe. Si demain la règle change, vous la corrigez à un seul endroit : dans le setter ou dans le constructeur.
private pour les protéger.
Vous exposez ensuite des méthodes public utiles
(getters, setters, et éventuellement d’autres actions).
Enfin, vous placez les contrĂ´les dans les setters et les constructeurs :
c’est là que vous refusez une valeur incorrecte, par exemple un âge négatif.
2.5static et final
Jusqu’ici, chaque objet avait ses propres données : le nom d’Amine, l’âge de Sara…
Mais parfois, une information concerne toutes les personnes Ă la fois.
C’est le rôle de static : appartenir à la classe, et non à une seule instance.
2.5.1 – Attributs static
Un attribut static n’est pas copié dans chaque objet.
Il existe une seule fois, partagée par toutes les instances.
Imaginez un compteur : chaque fois que vous créez une personne, le compteur augmente.
Ce compteur ne « vit » pas dans Amine ou dans Sara : il vit au niveau de la classe
Personne.
public class Personne {
private String nom;
private String prenom;
private int age;
private static int compteur = 0;
public Personne(String nom, String prenom, int age) {
this.nom = nom;
this.prenom = prenom;
this.age = age;
compteur++;
}
public static int getCompteur() {
return compteur;
}
}
public class MainClass {
public static void main(String[] args) {
Personne a = new Personne("Tazi", "Amine", 22);
Personne b = new Personne("Idrissi", "Sara", 20);
System.out.println(Personne.getCompteur()); // 2
}
}
2.5.2 – Méthodes static
Une méthode static s’appelle directement sur la classe :
Personne.getCompteur().
Vous n’avez pas besoin d’un objet pour l’utiliser.
En revanche, une méthode static ne peut pas utiliser this,
ni lire un attribut d’instance comme nom ou age.
Elle ne « connaît » pas une personne en particulier : elle travaille au niveau de la classe.
main est static pour cette raison.
La JVM la lance sans créer d’objet au préalable.
2.5.3 – Constantes avec static final
Quand une valeur ne doit jamais changer et qu’elle est commune à toute la classe,
vous combinez static et final.
Vous obtenez une constante de classe.
Par convention, son nom s’écrit en MAJUSCULES_AVEC_UNDERSCORES : ainsi, on voit tout de suite qu’il s’agit d’une constante.
public class Personne {
private int age;
public static final int AGE_MAJORITE = 18;
public boolean estMajeur() {
return age >= AGE_MAJORITE;
}
}
2.5.4 – Différence entre instance et classe
Le point important à bien comprendre, c’est le contexte d’exécution.
Une méthode static appartient à la classe.
Une méthode d’instance appartient à un objet précis.
Ce n’est donc pas le même cadre de travail.
Comparez d’abord ce qui appartient à un objet et ce qui appartient à la classe :
| Instance | Classe (static) |
|
|---|---|---|
| Appartenance | Chaque objet a sa copie | Une seule copie, partagée |
| Appel | amine.afficher() |
Personne.getCompteur() |
Accès à this |
Oui | Non |
La règle fondamentale
Cette différence de contexte entraîne une règle simple sur les accès :
| Depuis une méthode… | Variable d’instance | Méthode d’instance | Variable static | Méthode static |
|---|---|---|---|---|
| static | ❌ Non | ❌ Non | ✅ Oui | ✅ Oui |
| d’instance | ✅ Oui | ✅ Oui | ✅ Oui | ✅ Oui |
En d’autres termes : une méthode static ne peut pas utiliser directement les données d’un objet particulier, car elle ne sait pas de quel objet il s’agit. Une méthode d’instance, elle, peut tout utiliser : les attributs de l’objet courant, et aussi les éléments static de la classe.
public class Personne {
private String nom;
private static int compteur = 0;
public void afficher() {
System.out.println(nom); // OK : méthode d’instance → attribut d’instance
System.out.println(compteur); // OK : méthode d’instance → static aussi
}
public static int getCompteur() {
// System.out.println(nom); // ❌ erreur : static → pas d’attribut d’instance
return compteur; // OK : static → static
}
}
this).
C’est pour cela qu’une méthode static ne peut pas « voir » nom ou age
d’une personne particulière.
2.6Références et null
Une variable de type objet ne contient pas l’objet lui-même. Elle contient une référence : en quelque sorte, l’adresse de l’objet en mémoire. Cette idée explique plusieurs situations fréquentes.
2.6.1 – Plusieurs références vers un même objet
Deux variables peuvent désigner le même objet. Si vous modifiez l’objet via l’une, l’autre voit le même changement — car il n’y a qu’un seul objet en mémoire.
public class MainClass {
public static void main(String[] args) {
Personne a = new Personne("Tazi", "Amine", 22);
Personne b = a; // b pointe vers le mĂŞme objet que a
b.setAge(23);
System.out.println(a.getAge()); // 23
}
}
2.6.2 – null
Parfois, une variable d’objet n’a encore aucune cible.
Dans ce cas, elle vaut null : elle ne pointe vers aucun objet.
public class MainClass {
public static void main(String[] args) {
Personne p = null; // pas encore d’objet
}
}
2.6.3 – NullPointerException
Si vous demandez quelque chose à une référence null,
Java ne peut pas répondre : il n’y a pas d’objet derrière.
À l’exécution, vous obtenez une NullPointerException.
public class MainClass {
public static void main(String[] args) {
Personne p = null;
p.afficher(); // ❌ NullPointerException
}
}
Le même piège apparaît avec un tableau d’objets.
new Personne[5] crée un tableau de 5 cases, mais chaque case vaut
encore null : aucun objet Personne n’a été créé.
public class MainClass {
public static void main(String[] args) {
Personne[] liste = new Personne[5];
liste[0].setNom("Tazi"); // ❌ NullPointerException : liste[0] vaut null
}
}
Pour que cela fonctionne, créez d’abord l’objet, puis placez-le dans le tableau :
public class MainClass {
public static void main(String[] args) {
Personne[] liste = new Personne[5];
liste[0] = new Personne("Tazi", "Amine", 22);
liste[0].setNom("Alaoui"); // OK
}
}
if (p != null) { … }.
2.6.4 – Copie d’une référence vs copie d’un objet
Attention à ne pas confondre deux opérations très différentes.
L’affectation Personne b = a; copie la référence :
vous avez toujours un seul objet, avec deux noms pour le désigner.
Si vous voulez un nouvel objet indépendant, vous devez le créer
(par exemple avec un nouveau new).
Ensuite, modifier b ne change plus a.
public class MainClass {
public static void main(String[] args) {
Personne a = new Personne("Idrissi", "Sara", 20);
Personne b = new Personne(a.getNom(), a.getPrenom(), a.getAge()); // nouvel objet
b.setAge(21);
System.out.println(a.getAge()); // 20
System.out.println(b.getAge()); // 21
}
}
2.7Relations entre objets
Dans un vrai programme, les objets ne vivent pas isolés. Une personne a un contact, une adresse, parfois un véhicule… Voyons deux façons simples de relier des objets entre eux.
2.7.1 – Association entre objets
Une association relie deux objets qui peuvent exister l’un sans l’autre. Par exemple, Youssef peut avoir Fatima comme contact. Fatima existe déjà ; Youssef se contente de la référencer.
public class Personne {
private Personne contact; // association
public void setContact(Personne contact) {
this.contact = contact;
}
}
public class MainClass {
public static void main(String[] args) {
Personne youssef = new Personne("Alaoui", "Youssef", 25);
Personne fatima = new Personne("Benjelloun", "Fatima", 30);
youssef.setContact(fatima);
}
}
2.7.2 – Composition
La composition est une relation entre deux objets dans laquelle un objet est constitué d’un ou plusieurs autres objets.
L’idée peut se résumer ainsi : « un objet possède un autre objet comme partie de lui ».
Exemple simple : une voiture et son moteur. La voiture possède un moteur. Sans voiture, ce moteur n’a pas vraiment de sens dans ce modèle. En général, la voiture crée le moteur dans son constructeur.
public class Moteur {
private int puissance;
public Moteur(int puissance) {
this.puissance = puissance;
}
}
public class Voiture {
private String modele;
private Moteur moteur; // composition : la voiture possède un moteur
public Voiture(String modele, int puissance) {
this.modele = modele;
this.moteur = new Moteur(puissance); // créé avec la voiture
}
}
2.7.3 – Exemple : Personne et Adresse
Voici un autre exemple, avec notre fil conducteur. Quand vous créez Omar Chakir, vous créez aussi son adresse au même moment.
public class Adresse {
private String ville;
private String rue;
public Adresse(String ville, String rue) {
this.ville = ville;
this.rue = rue;
}
public String getVille() {
return ville;
}
}
public class Personne {
private String nom;
private String prenom;
private int age;
private Adresse adresse; // composition
public Personne(String nom, String prenom, int age, String ville, String rue) {
this.nom = nom;
this.prenom = prenom;
this.age = age;
this.adresse = new Adresse(ville, rue); // créée avec la personne
}
public void afficher() {
System.out.println(prenom + " " + nom + " — " + adresse.getVille());
}
}
public class MainClass {
public static void main(String[] args) {
Personne p = new Personne("Chakir", "Omar", 28, "Casablanca", "Bd Zerktouni");
p.afficher(); // Omar Chakir — Casablanca
}
}
2.8Méthodes de Object
En Java, toute classe hérite implicitement de Object.
Vous n’avez rien à écrire pour cela.
Trois méthodes reviennent très souvent : toString, equals
et hashCode. Voyons-les une par une.
2.8.1 – toString()
Quand vous affichez un objet avec System.out.println(p),
Java appelle automatiquement toString().
Sans redéfinition, le résultat est peu lisible.
En la redéfinissant, vous choisissez un texte clair.
public class Personne {
private String nom;
private String prenom;
private int age;
public Personne(String nom, String prenom, int age) {
this.nom = nom;
this.prenom = prenom;
this.age = age;
}
@Override
public String toString() {
return prenom + " " + nom + " (" + age + " ans)";
}
}
public class MainClass {
public static void main(String[] args) {
Personne p = new Personne("Tazi", "Amine", 22);
System.out.println(p); // Amine Tazi (22 ans)
}
}
2.8.2 – equals()
equals sert Ă comparer le contenu de deux objets
selon la règle que vous choisissez.
Par défaut, dans Object, elle se comporte comme == :
elle vérifie si c’est exactement la même instance en mémoire.
Pour vos classes, vous la redéfinissez souvent. Par exemple : deux personnes sont égales si elles ont le même nom, le même prénom et le même âge.
public class Personne {
private String nom;
private String prenom;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Personne autre = (Personne) o;
return age == autre.age
&& Objects.equals(nom, autre.nom)
&& Objects.equals(prenom, autre.prenom);
}
}
2.8.3 – == et equals()
Ne confondez pas ces deux outils :
| Opérateur / méthode | Compare |
|---|---|
== |
Les références : est-ce le même objet en mémoire ? |
equals |
Le contenu : selon la règle que vous avez définie |
equals.
Réservez == au test « est-ce exactement la même instance ? ».
2.8.4 – hashCode() : principe
hashCode() renvoie un entier associé à l’objet.
Pour l’instant, retenez surtout le contrat suivant :
si deux objets sont égaux avec equals,
ils doivent avoir le mĂŞme hashCode.
Les collections comme HashMap s’appuient sur ce contrat
(vous le reverrez au chapitre 4).
public class Personne {
private String nom;
private String prenom;
private int age;
@Override
public int hashCode() {
return Objects.hash(nom, prenom, age);
}
}
equals,
redéfinissez aussi hashCode de façon cohérente.
2.9enum avancé
Au chapitre 1, vous avez vu l’enum comme une liste de valeurs fixes. Ici, nous allons plus loin : un enum peut aussi porter des données et des méthodes, presque comme une petite classe.
2.9.1 – Attributs et constructeur
Chaque constante de l’enum peut avoir ses propres informations. Par exemple, un statut peut avoir un libellé lisible : « Étudiant », « Salarié »…
Le constructeur de l’enum est implicitement private.
Vous ne faites pas de new Statut(...) :
les constantes sont créées une fois pour toutes dans l’enum.
public enum Statut {
ETUDIANT("Étudiant"),
SALARIE("Salarié"),
RETRAITE("Retraité");
private final String libelle;
Statut(String libelle) {
this.libelle = libelle;
}
public String getLibelle() {
return libelle;
}
}
2.9.2 – Méthodes d’un enum
Vous appelez les méthodes sur une constante, comme sur un objet :
Statut.ETUDIANT.getLibelle().
Vous pouvez aussi ajouter vos propres méthodes métier.
public enum Statut {
ETUDIANT("Étudiant"),
SALARIE("Salarié");
private final String libelle;
Statut(String libelle) {
this.libelle = libelle;
}
public String description() {
return name() + " → " + libelle;
}
}
public class MainClass {
public static void main(String[] args) {
System.out.println(Statut.SALARIE.description());
// SALARIE → Salarié
}
}
2.10Synthèse et exercice pratique
2.10.1 – Création d’un modèle simple
Il est temps de rassembler les idées du chapitre.
Vous allez construire un petit modèle cohérent :
une classe Personne encapsulée, liée à une Adresse,
avec toString, equals et hashCode.
2.10.2 à 2.10.5 – Cahier des charges
Exercice de synthèse
-
Modèle : créez
Adresse((ville, rue)etPersonne(nom, prenom, age, adresse). - Objets : instanciez deux personnes (par exemple Youssef Alaoui et Fatima Benjelloun).
-
Encapsulation : attributs
private, getters / setters avec contrôle de l’âge (≥ 0). -
Relation : composition
Personne → Adresse(l’adresse est créée avec la personne). -
Object : redéfinissez
toStringetequals(deux personnes égales si même nom, prénom et âge). Ajoutez unhashCodecohérent.
En résumé
- Une classe est un modèle ; un objet est une instance concrète de ce modèle.
newcrée l’objet ; la variable contient une référence.- Les constructeurs initialisent ;
thisetthis(...)clarifient et factorisent. - L’encapsulation protège l’état :
private+ getters / setters contrôlés. staticappartient à la classe ;static finalnomme une constante.- Association et composition relient les objets ;
toString/equals/hashCodestructurent l’affichage et la comparaison.