Programmation orientée objet · C1 Du procédural à l'objet · Chapitre 2 · 5 h
Classes et objets
La classe comme moule et l'objet comme instance ; attributs, méthodes, constructeur, référence this, création par new, cycle de vie d'un objet.
Compte a = new Compte(100);Compte b = new Compte(50);a.deposer(30);System.out.println(b.getSolde()); /* 50, pas 80 */Cette évidence n'en est pas une au premier contact. La question « mais alors, il y a deux
soldes ? » revient chaque année, et elle est légitime : le code de deposer est écrit une
seule fois, et l'on peine à voir comment une seule ligne peut modifier une case sans toucher à
l'autre.
C'est le premier point qui coince du semestre. Il se règle d'une seule façon : en regardant plusieurs objets côte à côte, pas à pas.
Le moule et la gaufre
Une classe décrit ce qu'un objet contiendra et ce qu'il saura faire. Elle n'existe qu'en un exemplaire, et elle ne contient aucune donnée d'utilisateur — c'est un moule.
Un objet, ou instance, est une chose fabriquée avec ce moule. Il possède ses propres attributs, distincts de ceux de tous les autres objets de la même classe.
L'analogie tient si l'on ne l'étire pas : le moule à gaufres ne contient pas de pâte, et deux gaufres issues du même moule ont la même forme et des garnitures différentes.
public class Compte { private double solde; // attribut : une case PAR objet private String titulaire; public Compte(String titulaire, double depotInitial) { // constructeur this.titulaire = titulaire; this.solde = depotInitial; } public void deposer(double montant) { // méthode this.solde += montant; } public double getSolde() { return this.solde; }}Trois vocabulaires à fixer. Les attributs — ou champs — sont les variables déclarées dans la classe : chaque objet en reçoit un jeu complet. Les méthodes sont les fonctions déclarées dans la classe : elles existent une seule fois en mémoire, et reçoivent l'objet sur lequel elles travaillent. Le constructeur est la méthode particulière appelée à la création.
Une différence avec le C mérite d'être signalée tout de suite : en Java, les attributs sont
initialisés automatiquement — 0 pour les nombres, false pour les booléens, null pour les
références. Ce n'est pas le cas des variables locales, qui restent non initialisées comme au
chapitre 2 du cours de programmation, et le compilateur refuse de les lire.
Le constructeur
public Compte(String titulaire, double depotInitial) { ... }Trois règles, et la troisième surprend.
Il porte exactement le nom de la classe, majuscule comprise. Il n'a aucun type de
retour, pas même void — écrire void Compte(...) en fait une méthode ordinaire nommée
Compte, qui compile et n'est jamais appelée par new. C'est une erreur classique, et elle est
silencieuse.
Il sert à établir l'invariant de l'objet : à la sortie du constructeur, l'objet doit être dans un état valide. Un compte sans titulaire ou avec un solde négatif ne doit pas pouvoir exister — et c'est le constructeur qui l'empêche, comme le chapitre 3 le formalisera.
Enfin, une règle de Java qu'il vaut mieux connaître : si l'on n'écrit aucun constructeur, le
compilateur en fournit un par défaut, sans paramètre. Dès qu'on en écrit un seul, ce
constructeur par défaut disparaît — et new Compte() cesse de compiler. Il faut alors le
réécrire si on en veut un.
Ce que fait new
L'instruction Compte a = new Compte("Ana", 100); fait quatre choses, dans cet ordre.
1. ALLOUER un objet sur le TAS, avec une case par attribut2. INITIALISER ces cases aux valeurs par défaut (0, false, null)3. EXÉCUTER le constructeur, qui les remplit vraiment4. RENDRE une RÉFÉRENCE vers cet objet, qui est rangée dans aLe mot important est le dernier. a ne contient pas l'objet, mais une référence vers lui —
c'est-à-dire, en pratique, une adresse au sens du chapitre 7 du cours de programmation. L'objet
vit sur le tas, la variable a vit sur la pile.
D'où une conséquence que le chapitre 4 exploitera :
Compte a = new Compte("Ana", 100);Compte b = a; // COPIE LA RÉFÉRENCE, pas l'objetb.deposer(50);a.getSolde(); // 150 : c'est le MÊME objetIl n'y a ici qu'un seul compte, désigné par deux noms. C'est exactement le passage par adresse du cours de programmation, et c'est ce qui distingue Java du C sur ce point : en Java, tout objet est manipulé par référence, sans qu'aucune étoile ne l'écrive.
this
Animation · 8 étapes
Deux objets, deux soldes — mais un seul compteur
- Avant toute création — La classe est un MOULE : elle décrit ce qu'un compte contiendra, sans qu'aucun compte existe. Une seule chose existe déjà, la variable de classe `nombre` — elle appartient à la classe, pas à un objet, et il n'y en aura jamais qu'un exemplaire.
- new alloue le premier objet — `new` fait deux choses : il réserve la mémoire d'un objet — donc une case `solde` bien à lui — puis il appelle le constructeur. Pendant cet appel, `this` désigne cet objet-là, et rien d'autre.
- this.solde = depot — `solde` sans préfixe désignerait ici le paramètre le plus proche ; `this.solde` lève l'ambiguïté et désigne l'attribut de l'objet en cours de construction. C'est le premier usage de `this`, et le plus fréquent.
- nombre++ — sans this — Aucun `this` ici, et ce n'est pas un oubli : `nombre` n'appartient à aucun objet. On aurait pu écrire `Compte.nombre++`, ce qui est plus clair et devrait être l'habitude.
- Second new : un autre objet — Une nouvelle case `solde` est allouée, indépendante de la première. `this` désigne maintenant b — même code, autre objet, et c'est exactement ce que le mot `instance` veut dire.
- La MÊME ligne, un AUTRE solde — La ligne 5 s'exécute pour la seconde fois et écrit 50 — sans toucher au 100 de a. Deux instances, deux jeux d'attributs : c'est le point que la lecture du code seul ne montre pas.
- Le compteur, lui, est partagé — `nombre` passe à 2. Il n'y a pas « le nombre de a » et « le nombre de b » : il y a UN compteur, que les deux constructeurs ont incrémenté. C'est toute la différence entre variable de classe et variable d'instance.
- Modifier un objet ne touche pas l'autre — a.solde change, b.solde ne bouge pas, et nombre non plus. Trois cases distinctes, trois durées de vie distinctes — et le constructeur est terminé, donc `this` ne désigne plus rien.
this désigne l'objet sur lequel la méthode s'exécute. Il n'a de sens qu'à l'intérieur
d'une méthode d'instance, et il change à chaque appel — c'est ce que l'animation montre.
Trois usages, du plus fréquent au plus rare.
Lever une ambiguïté. Quand un paramètre porte le même nom qu'un attribut, solde désigne le
paramètre — le plus proche — et this.solde l'attribut. C'est le cas du constructeur ci-dessus,
et c'est l'usage qu'on rencontre partout. Certains l'évitent en nommant les paramètres
différemment ; l'écrasante majorité des codes Java fait l'inverse et emploie this.
Se passer soi-même à une autre méthode : journal.enregistrer(this).
Enchaîner les constructeurs : this(titulaire, 0) appelle un autre constructeur de la même
classe, ce qui évite de dupliquer l'initialisation. L'appel doit être la première
instruction.
Le contre-emploi à connaître : dans une méthode statique, this n'existe pas — il n'y a
aucun objet courant. C'est le sujet du chapitre 4, et l'erreur non-static variable this cannot be referenced from a static context est celle que tout étudiant rencontre en écrivant son
premier main.
Quiz · 1 question
Après Compte a = new Compte(100); Compte b = a; b.deposer(50); que vaut a.getSolde() ?
- 100 : a et b sont deux variables distinctes, donc deux comptes distincts — 100
- 150 : b = a copie la RÉFÉRENCE, pas l'objet — il n'y a qu'un seul compte, désigné par deux noms — 150
- Une erreur : on ne peut pas affecter un objet à une autre variable sans le cloner — erreur
Réponse : new fait quatre choses : allouer l'objet sur le TAS, initialiser ses cases aux valeurs par défaut, exécuter le constructeur, et rendre une RÉFÉRENCE. C'est cette référence — une adresse, au sens du cours de programmation — qui est rangée dans a. L'affectation b = a recopie donc l'adresse, pas l'objet : les deux variables désignent la même case sur le tas, et un dépôt fait par l'une est visible par l'autre. Pour obtenir deux comptes indépendants, il faut deux new. C'est la différence entre l'IDENTITÉ (est-ce le même objet ?) et l'ÉGALITÉ (ont-ils le même contenu ?), que le chapitre 6 traitera avec equals — et c'est aussi ce qui rend le passage de paramètres subtil au chapitre 4.
Cycle de vie
Un objet naît avec new, vit tant qu'une référence le désigne, et meurt quand plus aucune ne le
désigne.
Compte a = new Compte("Ana", 100);a = new Compte("Bo", 50); // le premier compte n'est plus atteignableLe premier objet existe toujours en mémoire, mais plus personne ne peut l'atteindre : c'est exactement la définition de la fuite du chapitre 8 du cours de programmation. La différence est que Java dispose d'un ramasse-miettes (garbage collector), qui repère périodiquement les objets inatteignables et récupère leur mémoire.
Trois conséquences pratiques, et la dernière est une mise en garde.
Il n'y a pas de free, ni de destructeur à écrire. La question « qui libère ? », qui
occupait tout le chapitre 8 du C, ne se pose plus.
Le prix est un coût à l'exécution : le ramasse-miettes s'exécute quand il le décide, et peut introduire de brèves pauses. C'est acceptable presque partout, et rédhibitoire en temps réel dur — au sens du chapitre 1 du cours de systèmes.
Enfin, le ramasse-miettes ne supprime pas les fuites, il supprime une de leurs causes. Un objet encore référencé par une liste qu'on a oublié de vider ne sera jamais récupéré. La fuite en Java n'est pas « j'ai oublié de libérer » mais « je garde une référence dont je n'ai plus besoin » — plus subtile, et plus difficile à voir.
Quiz · 1 question
Un étudiant écrit public void Compte(String nom) puis appelle new Compte(« Ana »), et le code ne compile pas. Pourquoi ?
- Il manque le mot-clé static devant la méthode — static manquant
- Un constructeur n'a AUCUN type de retour, pas même void : avec void, c'est une méthode ordinaire nommée Compte, jamais appelée par new — et comme un constructeur a été « écrit », celui par défaut n'existe plus — void en trop
- Le nom du paramètre ne doit pas être identique à celui de l'attribut — conflit de noms
Réponse : C'est l'erreur silencieuse la plus classique du chapitre. Un constructeur se reconnaît à deux signes : il porte exactement le nom de la classe, et il n'a AUCUN type de retour. Ajouter void en fait une méthode ordinaire — qui compile parfaitement, puisque rien n'interdit une méthode nommée Compte — mais que new n'appellera jamais. Le message d'erreur porte alors sur l'appel : « constructor Compte in class Compte cannot be applied to given types », parce que le seul constructeur disponible est celui par défaut, sans paramètre... sauf que celui-ci a disparu dès qu'on a écrit un constructeur — ce qui n'est justement PAS le cas ici, aucun n'ayant été écrit. D'où un message déroutant. Quant au conflit de noms entre paramètre et attribut, il est non seulement légal mais idiomatique : c'est précisément l'usage principal de this.
À vous
L'exercice met deux objets côte à côte et affiche leur état après chaque instruction — c'est l'animation, mais que vous pilotez.
Quatre temps. Créer deux comptes et vérifier qu'un dépôt sur l'un ne touche pas l'autre. Écrire
un constructeur qui valide son paramètre, avant même le chapitre 3. Faire l'expérience de
l'alias : b = a, un dépôt, et les deux soldes. Enfin, deux pièges de this — un attribut
masqué par un paramètre quand on oublie this, et une méthode détachée de son objet, qui
montre que this n'est pas fixé une fois pour toutes.
Exercice de code
Mettez deux objets côte à côte, validez dans le constructeur, puis faites tomber les deux pièges de this.
Point de départ
// JavaScript possède class, constructor et this : la transposition depuis
// Java est directe, à la déclaration des types près.
class Compte {
constructor(titulaire, depotInitial) {
// ← à écrire : refuser un dépôt initial négatif, puis initialiser
this.titulaire = titulaire;
this.solde = depotInitial;
}
deposer(montant) { this.solde += montant; }
getSolde() { return this.solde; }
toString() { return this.titulaire + " : " + this.solde.toFixed(2); }
}
// Affiche l'état de plusieurs objets CÔTE À CÔTE, comme au tableau.
function etat(etiquette, ...comptes) {
console.log(" " + etiquette.padEnd(28) +
comptes.map((c) => c.toString()).join(" | "));
}
// ── 1. Deux objets, deux jeux d'attributs ─────────────────────────────────
const a = new Compte("Ana", 100);
const b = new Compte("Bo", 50);
etat("après les deux new", a, b);
a.deposer(30);
etat("après a.deposer(30)", a, b);
// ── 2. L'alias ────────────────────────────────────────────────────────────
const c = a; // COPIE LA RÉFÉRENCE, pas l'objet
c.deposer(20);
etat("après c.deposer(20)", a, b);
console.log(" a et c sont le même objet :", a === c);
// ── 3. L'attribut masqué ──────────────────────────────────────────────────
class CompteFautif {
constructor(titulaire, solde) {
titulaire = titulaire; // ← sans this : on affecte le paramètre à lui-même
solde = solde;
}
toString() { return this.titulaire + " : " + this.solde; }
}
// ── À VOUS ────────────────────────────────────────────────────────────────
// 1. Faites refuser un dépôt initial négatif par le constructeur.
// 2. Créez un CompteFautif et affichez-le : que valent ses attributs, et
// pourquoi le code compile-t-il sans rien dire ?
// 3. Détachez la méthode : const f = a.deposer; f(10). Que se passe-t-il,
// et qu'est-ce que cela apprend sur this ?
Solution
class Compte {
constructor(titulaire, depotInitial) {
// Le constructeur ÉTABLIT L'INVARIANT : à sa sortie, l'objet doit être
// dans un état valide. Un compte au solde négatif ne doit pas exister.
if (depotInitial < 0) throw new Error("dépôt initial négatif : " + depotInitial);
if (!titulaire) throw new Error("un compte doit avoir un titulaire");
this.titulaire = titulaire;
this.solde = depotInitial;
}
deposer(montant) { this.solde += montant; }
getSolde() { return this.solde; }
toString() { return this.titulaire + " : " + this.solde.toFixed(2); }
}
function etat(etiquette, ...comptes) {
console.log(" " + etiquette.padEnd(28) + comptes.map((c) => c.toString()).join(" | "));
}
console.log("— 1. deux objets, deux jeux d'attributs —");
const a = new Compte("Ana", 100);
const b = new Compte("Bo", 50);
etat("après les deux new", a, b);
a.deposer(30);
etat("après a.deposer(30)", a, b);
console.log(" Le code de deposer est écrit UNE fois ; il a modifié une case et");
console.log(" pas l'autre, parce que this désignait a et non b.");
console.log("");
console.log("— l'invariant tient dès la construction —");
try { new Compte("Cy", -10); } catch (e) { console.log(" refusé : " + e.message); }
console.log("");
console.log("— 2. l'alias —");
const c = a;
c.deposer(20);
etat("après c.deposer(20)", a, b);
console.log(" a === c :", a === c, " — un seul objet, deux noms");
const d = new Compte(a.titulaire, a.getSolde()); // là, une vraie copie
d.deposer(1000);
etat("après d.deposer(1000)", a, d);
console.log(" d est un AUTRE objet : il a fallu un second new.");
console.log("");
console.log("— 3. l'attribut masqué —");
class CompteFautif {
constructor(titulaire, solde) {
// « solde = solde » affecte le paramètre à lui-même : l'attribut n'est
// jamais créé. Aucune erreur — c'est du code parfaitement légal, et
// c'est ce qui le rend dangereux.
titulaire = titulaire;
solde = solde;
}
toString() { return this.titulaire + " : " + this.solde; }
}
console.log(" " + new CompteFautif("Dee", 500).toString());
console.log(" Les deux attributs valent undefined : le constructeur n'a rien");
console.log(" initialisé. En Java, le code compile aussi — les attributs y");
console.log(" prennent simplement leur valeur par défaut, 0 et null.");
console.log("");
console.log("— 4. la méthode détachée —");
const f = a.deposer;
try { f(10); } catch (e) { console.log(" f(10) échoue : " + e.message.split("\n")[0]); }
const g = a.deposer.bind(a);
g(10);
etat("après g(10), lié à a", a);
console.log(" this n'est pas fixé une fois pour toutes à la définition : il est");
console.log(" déterminé par l'APPEL. En Java le problème ne se pose pas ainsi —");
console.log(" on ne détache pas une méthode de son objet — mais le principe est");
console.log(" le même : une méthode reçoit l'objet sur lequel elle travaille.");
En travaux pratiques
Travaux pratiques 2 · 3 h
Le premier point qui coince : this
Faire disparaître la confusion entre la classe et l'objet, et entre le paramètre et le champ — en provoquant les deux erreurs plutôt qu'en les évitant.
Avant de commencer
- Le TP 1
Énoncé
- Deux objets — Créez deux Document et modifiez le titre du premier. Affichez les deux. Puis affichez ce que vaut une variable statique modifiée sur l'un des deux.
- Le constructeur qui ne fait rien — Écrivez un constructeur dont les paramètres portent le même nom que les champs, SANS écrire this. Créez un objet et affichez ses champs. Expliquez. Indice : Le paramètre masque le champ : l'affectation se fait du paramètre vers lui-même.
- Le réparer, deux fois — Corrigez d'abord en renommant les paramètres, puis avec this. Dites laquelle des deux formes vous garderez et pourquoi.
- this dans une méthode — Écrivez une méthode qui renvoie l'objet lui-même, et enchaînez trois appels sur une seule ligne. Expliquez ce que vaut this à chaque appel.
- Plusieurs constructeurs — Écrivez trois constructeurs de Document, dont deux qui délèguent au troisième. Vérifiez que la validation n'est écrite qu'une fois.
- L'objet non initialisé — Déclarez une référence sans l'affecter et appelez une méthode dessus. Lisez le message, et distinguez « la référence est nulle » de « l'objet est vide ».
- Comparer deux objets — Créez deux Document de contenu identique et comparez-les avec l'opérateur d'égalité. Expliquez le résultat, et faites de même avec deux chaînes construites de deux façons.
- Au fil rouge — Écrivez la classe Mediatheque contenant un tableau de Document, avec ajouter, chercher et lister. Vérifiez qu'aucune méthode n'a besoin de connaître l'intérieur de Document.
C'est réussi quand
- Vous expliquez pourquoi le constructeur sans this laisse les champs à null
- Vos trois constructeurs ne dupliquent aucune validation
- Vous savez dire ce que compare l'opérateur d'égalité sur deux objets
Correction
public Document(String titre, String auteur) {
titre = titre; /* le PARAMÈTRE est affecté au PARAMÈTRE */
auteur = auteur; /* le champ n'est jamais touché */
}
new Document("1984", "Orwell").getTitre() → null
/* javac avec -Xlint signale : self-assignment */Le paramètre MASQUE le champ dans toute la portée du constructeur : le nom titre y désigne le paramètre, jamais le champ. C'est l'erreur numéro un des débuts en Java, et elle ne provoque aucune erreur de compilation — seulement des champs à null constatés plus tard.
public Document(String titre, String auteur) {
this.titre = titre; /* this.titre : le CHAMP de l'objet courant */
this.auteur = auteur; /* titre : le PARAMÈTRE */
}
/* this = « l'objet sur lequel la méthode a été appelée » */
Document a = new Document("1984", "Orwell");
Document b = new Document("Dune", "Herbert");
a.getTitre(); /* dans getTitre, this vaut a */
b.getTitre(); /* dans getTitre, this vaut b */Une seule méthode existe en mémoire, pas une par objet : this est le paramètre implicite qui dit sur QUI elle travaille. Une fois cela compris, l'essentiel du chapitre s'éclaire — y compris pourquoi une méthode static, qui n'a pas de this, ne peut pas accéder aux champs d'instance. Garder les mêmes noms et écrire this est la convention : renommer en titreParam fonctionne, et signale que l'on n'a pas compris le mécanisme.
public Document avecAnnee(int annee) {
this.annee = annee;
return this; /* on rend l'objet lui-même */
}
Document d = new Document("Dune", "Herbert")
.avecAnnee(1965)
.avecGenre("science-fiction")
.avecCote("SF-HER-01");
/* à chaque appel, this est le MÊME objet : un seul Document existe */L'enchaînement n'est possible que parce que chaque méthode renvoie this. C'est la base du motif constructeur fluide, très employé en Java. Attention toutefois : cet objet est MUTABLE, et le rendre au milieu d'une chaîne le rend modifiable par n'importe qui — une variante renvoie une copie modifiée plutôt que this.
public Document(String titre, String auteur, int annee) {
if (titre == null || titre.isBlank())
throw new IllegalArgumentException("titre obligatoire");
if (annee < 1400 || annee > 2100)
throw new IllegalArgumentException("année invalide : " + annee);
this.titre = titre; this.auteur = auteur; this.annee = annee;
}
public Document(String titre, String auteur) {
this(titre, auteur, 0); /* this(...) : DÉLÉGATION */
}
public Document(String titre) {
this(titre, "inconnu", 0);
}this(...) en première instruction appelle un autre constructeur de la même classe. Toute la validation vit alors à UN seul endroit : ajouter une règle la fait respecter par les trois formes. Sans cette délégation, on recopie la validation trois fois, et la troisième copie finit toujours par diverger.
Document d; /* champ non initialisé → null */
d.getTitre(); /* NullPointerException */
/* null = AUCUN objet. Différent d'un objet aux champs vides. */
Document a = new Document("1984", "Orwell");
Document b = new Document("1984", "Orwell");
a == b → false /* deux objets DISTINCTS, deux adresses */
a.equals(b) → false /* tant qu'on n'a pas redéfini equals (TP 6) */
String s1 = "abc", s2 = "abc";
s1 == s2 → true /* littéraux INTERNÉS : même objet */
String s3 = new String("abc");
s1 == s3 → false /* d'où : TOUJOURS equals sur les chaînes */L'opérateur d'égalité compare des RÉFÉRENCES — sont-ce le même objet — jamais des contenus. Le cas des chaînes est le plus traître, parce que l'internement des littéraux le fait fonctionner par accident dans les cas de test, et échouer dès que la chaîne vient d'une lecture ou d'une concaténation. La redéfinition d'equals attend au TP 6.
Ce que la suite en fait
Le chapitre 3 ferme l'objet. Rien n'empêche encore d'écrire a.solde = -1000 de l'extérieur, ce
qui ruine tout le bénéfice de l'introduction : la règle est dans la méthode, mais on peut passer
à côté. L'encapsulation supprime ce contournement.
Le chapitre 4 reprendra la distinction que l'animation a montrée — variable de classe contre
variable d'instance — et la traitera pour elle-même, avec les méthodes statiques et le main.
À retenir
Flashcards · 5 cartes
- Quelle est la différence entre une classe et un objet ?
- La CLASSE décrit ce qu'un objet contiendra et saura faire : elle n'existe qu'en un exemplaire et ne porte aucune donnée d'utilisateur — c'est un moule. L'OBJET, ou instance, est fabriqué avec ce moule et possède ses PROPRES attributs, distincts de ceux de toutes les autres instances. Les méthodes, elles, n'existent qu'une fois en mémoire : elles reçoivent l'objet sur lequel elles travaillent.
- Quelles sont les trois règles du constructeur en Java ?
- 1) Il porte EXACTEMENT le nom de la classe. 2) Il n'a AUCUN type de retour, pas même void — écrire void en fait une méthode ordinaire que new n'appellera jamais, erreur silencieuse classique. 3) Il établit l'INVARIANT : à sa sortie, l'objet doit être dans un état valide. Et une règle de Java : si l'on n'écrit aucun constructeur, le compilateur en fournit un par défaut sans paramètre — mais dès qu'on en écrit un, celui-là disparaît.
- Que fait new, et pourquoi b = a ne copie-t-il pas l'objet ?
- new ALLOUE l'objet sur le tas (une case par attribut), INITIALISE ces cases aux valeurs par défaut (0, false, null), EXÉCUTE le constructeur, et REND UNE RÉFÉRENCE. C'est cette référence — une adresse — qui est rangée dans la variable, laquelle vit sur la pile. Donc b = a recopie l'adresse : un seul objet, deux noms, et un dépôt par l'un est visible par l'autre. Pour deux objets indépendants, il faut deux new.
- Quels sont les trois usages de this, et où n'existe-t-il pas ?
- this désigne l'objet sur lequel la méthode s'exécute, et change à chaque appel. 1) LEVER UNE AMBIGUÏTÉ quand un paramètre porte le nom d'un attribut — l'usage principal. 2) SE PASSER SOI-MÊME à une autre méthode. 3) ENCHAÎNER LES CONSTRUCTEURS avec this(...), qui doit être la première instruction. Il n'existe PAS dans une méthode statique, où il n'y a aucun objet courant — d'où l'erreur classique du premier main.
- Comment un objet Java meurt-il, et le ramasse-miettes supprime-t-il les fuites ?
- Il vit tant qu'une référence le désigne, et devient récupérable quand plus aucune ne le fait. Le ramasse-miettes repère périodiquement les objets inatteignables : il n'y a donc ni free ni destructeur, et la question « qui libère ? » disparaît. Prix : un coût à l'exécution, avec de brèves pauses — rédhibitoire en temps réel dur. Et il NE SUPPRIME PAS les fuites : un objet encore référencé par une liste qu'on a oublié de vider ne sera jamais récupéré. La fuite Java, c'est « je garde une référence inutile ».