- Apprendre
- Informatique et numérique
- Programmation
- Java orienté objet : classes, collections, héritage et tests
- Références et égalité en Java
Objets, références et égalité en Java
null, transmets des objets à des méthodes et renvoie-les, puis construis des classes à partir d’autres objets et de listes d’objets. Écris ensuite equals et hashCode pour que les listes, les ensembles et les Map comparent tes objets par leur contenu, et sépare l’interface textuelle d’un programme de sa logique.DébutantUnité 6 sur 11 de la formation Formation : Java orienté objet : classes, collections, héritage et tests
Ce que tu vas apprendre
- Explique ce que contient une variable Java de type primitif ou de type référence, prévois l’effet d’une affectation sur chacune et manipule les variables qui ne référencent aucun objet.
- Compare les variables de type primitif et de type référenceNomme les types primitifs de Java, explique qu’une variable de type référence contient une référence à un objet et prévois l’effet de la copie de chaque type de variable.
- Suis les alias, les réaffectations et les objets devenus inaccessiblesSuis plusieurs variables qui référencent un même objet, distingue la modification d’un objet de la réaffectation d’une variable vers un autre objet, et explique ce qui arrive aux objets que plus rien ne référence.
- Manipule la référence nulle sans provoquer d’erreurExplique ce que signifie null, reconnais les endroits où cette valeur apparaît, évite NullPointerException et détermine quand null est une valeur pertinente.
- Compare les variables de type primitif et de type référence
- Explique comment Java transmet les arguments, écris des méthodes qui reçoivent et modifient des objets ou les comparent à un autre objet de la même classe, et renvoie sans risque des objets nouveaux ou existants.
- Utilise des objets comme paramètres de méthodesPrévois ce qu’une méthode peut ou non modifier lorsqu’elle reçoit une valeur primitive ou une référence, et écris des méthodes qui prennent des objets d’une autre classe ou de leur propre classe.
- Renvoie des objets depuis des méthodesÉcris des méthodes qui créent et renvoient de nouveaux objets ou renvoient des références à des objets existants, et évite d’exposer accidentellement l’état interne d’un objet.
- Utilise des objets comme paramètres de méthodes
- Conçois des classes dont les champs sont d’autres objets ou des listes d’objets, manipule des listes contenant des objets et décide quand les objets contenus doivent être partagés ou copiés.
- Construis une classe par composition avec d’autres objetsModélise des relations de possession avec des champs de type référence, délègue le travail aux objets contenus et décide si un objet contenu doit être partagé ou copié.
- Stocke et traite des objets dans des listesAjoute, trouve, modifie et supprime des objets dans une ArrayList, et explique pourquoi la liste contient des références et non des copies.
- Construis une classe autour d’une liste d’objetsÉcris une classe qui conserve une liste d’objets comme champ, l’initialise, propose des méthodes pour manipuler son contenu et garde la liste elle-même masquée.
- Construis une classe par composition avec d’autres objets
- Distingue l’identité de l’égalité de contenu, écris correctement les méthodes equals et hashCode et prévois comment les listes, les ensembles et les Map les utilisent.
- Distingue l’identité de l’égalité de contenuExplique ce que == et equals comparent pour les valeurs primitives, les chaînes, les objets enveloppes et tes propres classes, et prévois le résultat de chaque comparaison.
- Écris une méthode equalsRedéfinis equals avec la bonne signature et les bonnes étapes, compare correctement chaque type de champ et respecte le contrat général de la méthode.
- Associe hashCode à equalsExplique le contrat de hashCode, écris hashCode pour une classe qui redéfinit equals et prévois le comportement de HashSet et HashMap avec et sans cette méthode.
- Distingue l’identité de l’égalité de contenu
- Structure un programme textuel de sorte qu’une classe communique avec l’utilisateur et que d’autres classes contiennent les données et les règles, puis enrichis-le et vérifie-le sans mélanger les deux.
- Décompose un programme en une interface utilisateur et des classes de logique applicativeDétermine quel code appartient à l’interface utilisateur et quel code appartient à la logique, puis relie les deux par des paramètres de constructeur et une méthode de démarrage.
- Enrichis et vérifie un programme dont l’interface utilisateur et la logique applicative sont séparéesAjoute des fonctionnalités en enrichissant la logique et en l’appelant depuis l’interface, vérifie directement la logique avec des valeurs connues et garde une tâche précise pour chaque classe.
- Décompose un programme en une interface utilisateur et des classes de logique applicative
Références et égalité en Java : cours
1Suis les valeurs et les références
Une variable et un objet sont deux éléments différents d’un programme Java. Comprendre ce que contient chaque variable permet d’expliquer pourquoi une affectation crée des nombres indépendants tandis qu’une autre donne à deux noms accès au même objet. Commence par les valeurs copiées, puis suis les modifications instruction par instruction. La même représentation t’aidera à trouver les objets absents, à lire les erreurs liées à null et à décider si une modification doit être partagée.
1.1 Ce que contient une variable
Une variable Java contient une valeur. La question importante est de savoir si cette valeur est une donnée ordinaire ou une référence qui mène à un objet. Dans les deux cas, l’affectation copie cette valeur.
Données primitives et références d’objets
Java possède huit types primitifs : byte, short, int, long, float, double, char et boolean. Une variable de type primitif contient directement sa donnée. Une variable de type référence contient une référence à un objet ou la valeur spéciale null. Les classes comme String, Scanner et ArrayList, tes propres classes et les tableaux sont des types référence.
| Déclaration | Contenu de la variable |
|---|---|
int count = 5; | La valeur entière 5 |
Counter c = new Counter(); | Une référence à un nouveau compteur |
Counter c = null; | Aucune référence à un objet |
Dans new Counter(), Java crée un objet, exécute son constructeur et produit une référence. L’affectation stocke cette référence dans c. Dessine la variable sous forme de boîte avec une flèche menant à un objet distinct. Chaque évaluation de new crée un objet distinct, même lorsque les arguments sont identiques.
Copie la valeur, puis suis le résultat
Avec les types primitifs, modifier la variable copiée laisse la première variable inchangée :
int a = 5;
int b = a;
b = b + 1;
La dernière ligne lit 5 dans b, ajoute 1 et stocke 6 dans b. La valeur de a reste 5.
Pour un exemple avec une référence, utilise cette petite classe :
class Counter {
private int value;
Counter() { this.value = 0; }
Counter(int value) { this.value = value; }
void increase() { value++; }
int value() { return value; }
}
Suis maintenant ces instructions :
Counter first = new Counter();
Counter second = first;
second.increase();
System.out.println(first.value()); // 1
Seule la première ligne crée un compteur. La deuxième copie la référence : l’appel de méthode modifie donc l’unique compteur auquel les deux variables donnent accès. Pour créer un compteur indépendant avec les mêmes données, écris new Counter(first.value()). Copier une variable n’appelle pas de constructeur.
Classes enveloppes et chaînes immuables
Chaque type primitif possède une classe enveloppe, comme Integer, Double, Boolean ou Character. Les variables de type enveloppe contiennent des références ou null. Java peut convertir automatiquement une valeur primitive en objet enveloppe et inversement ; ces conversions sont appelées autoboxing et unboxing. Les listes contiennent des références : une liste d’entiers est donc une ArrayList<Integer>.
String est aussi un type référence, mais ses objets sont immuables : leurs caractères ne peuvent pas changer après leur création.
String s = "java";
String t = s;
t = t.toUpperCase();
Ici, s référence toujours « java » et t référence « JAVA ». La dernière ligne affecte le résultat à t ; elle ne modifie pas la chaîne originale partagée. Les méthodes de String peuvent réutiliser l’objet original lorsqu’aucune modification n’est nécessaire.
Afficher un objet utilise sa méthode toString(). Sans redéfinition, le résultat hérité prend une forme comme Counter@7e9e5f8a : nom de classe, @ et code de hachage hexadécimal. Définis toString() lorsque l’affichage doit montrer les données de l’objet.
Points clés
- Une affectation copie une valeur : donnée primitive ou référence à un objet.
- Chaque évaluation de
newcrée un objet distinct. - Copier une référence partage l’objet ; cela ne copie pas ses champs.
- Les classes enveloppes et les chaînes sont des types référence ; les chaînes sont immuables.
- Afficher un objet utilise
toString().
1.2 Objets partagés et flèches modifiées
Deux variables qui référencent le même objet sont des alias. Un alias donne un autre chemin vers des données existantes. Pour suivre un programme, distingue les variables des objets auxquels elles donnent accès.
Une mutation change les données ; une réaffectation change une variable
Une mutation modifie l’état d’un objet, par exemple son poids enregistré. Une réaffectation place une autre valeur dans une variable. Pour une variable de type référence, elle change donc sa flèche.
Utilise une boîte dont le poids est en kilogrammes :
class Box {
private double weight;
Box(double weight) { this.weight = weight; }
double getWeight() { return weight; }
void setWeight(double weight) { this.weight = weight; }
}
Suis cette séquence :
Box a = new Box(2);
Box b = a;
b.setWeight(5);
b = new Box(7);
- Dessine la variable
apointant vers l’objet A, de poids 2. - Dessine
bpointant vers ce même objet A. Il n’y a toujours qu’une boîte. - Remplace le poids dans l’objet A par 5. Les deux chemins donnent maintenant accès au poids 5.
- Crée l’objet B de poids 7 et redirige uniquement
bvers lui.
À la fin, a.getWeight() vaut 5 et b.getWeight() vaut 7. La quatrième instruction ne réinitialise pas l’objet A et ne redirige pas a vers l’objet B.
| Action | Modification dans le diagramme | Effet par les autres alias |
|---|---|---|
b.setWeight(5) | Modifier un champ dans l’objet | Ils voient le nouveau poids |
b = new Box(7) | Rediriger la flèche de b | Leurs flèches restent inchangées |
b = null | Supprimer la flèche de b | Ils donnent toujours accès à leurs objets |
L’accessibilité par référence détermine la durée de vie d’un objet
Un objet est accessible par référence tant que le programme en cours d’exécution peut suivre une chaîne de références utilisable jusqu’à lui. Cette chaîne peut passer par une variable, un champ d’objet ou un élément de liste. La disparition d’une variable locale ne suffit donc pas à rendre un objet inaccessible si une liste le contient encore.
Poursuis le suivi avec a = null;. Dans cet exemple, aucun chemin accessible ne mène plus à l’objet A, qui pèse 5 kg. Sa mémoire peut être récupérée par le mécanisme de récupération de mémoire. Java récupère automatiquement la mémoire des objets inaccessibles, mais le ramasse-miettes choisit le moment ; l’affectation ne garantit pas une récupération immédiate. L’objet B reste accessible par b.
Tu ne libères pas les objets Java manuellement. Lorsqu’un programme perd sa dernière référence utilisable vers un objet, il perd aussi la possibilité de consulter ou de modifier les données de cet objet.
Trouve les alias cachés
Des alias apparaissent lorsqu’un objet est ajouté à une liste, transmis à une méthode, renvoyé par une méthode ou stocké dans le champ d’un autre objet. Chaque opération peut transmettre une référence. Avant de modifier un objet reçu d’ailleurs, identifie les autres parties du code qui peuvent encore y accéder.
Un bogue courant commence par cette idée : « J’ai copié la variable, j’ai donc ma propre boîte. » Utilise un nouvel objet lorsque les modifications doivent être indépendantes. Pour diagnostiquer le bogue, compte séparément les créations d’objets et les affectations, nomme chaque objet une seule fois et traite chaque instruction dans l’ordre. Ne redessine pas un objet simplement parce qu’une deuxième variable pointe maintenant vers lui.
Points clés
- Les alias mènent à un même objet et partagent donc ses mutations.
- Une réaffectation redirige une variable et laisse les autres alias inchangés.
- Suis séparément les champs et les flèches des références.
- La mémoire d’un objet inaccessible peut être récupérée ; le moment n’est pas spécifié.
1.3 Aucun objet : null et vérifications sans risque
La valeur null signifie qu’une référence ne mène à aucun objet. Elle est utile pour un résultat absent, mais un programme doit gérer cette absence avant d’essayer d’utiliser un objet.
Absent et vide sont deux choses différentes
String text = ""; référence une véritable chaîne de longueur 0. String text = null; ne référence aucune chaîne : il n’y a donc aucun objet sur lequel appeler length().
Une variable de type primitif ne peut pas contenir null : int x = null; ne se compile pas. Une variable de type enveloppe peut le contenir : Integer x = null; se compile. Convertir cet objet enveloppe en valeur primitive avec int y = x; lève une NullPointerException, car l’unboxing nécessite un objet enveloppe.
Les champs reçoivent des valeurs par défaut lors de la création d’un objet. Les champs de type référence valent initialement null, les champs numériques 0 et les champs booléens false, jusqu’à ce que l’initialisation leur affecte d’autres valeurs. Les variables locales n’ont pas de valeur par défaut. Lire une variable locale avant de lui affecter une valeur provoque une erreur de compilation, comme « variable might not have been initialized ».
Trouve la référence manquante
Appeler une méthode d’instance par l’intermédiaire de null, accéder ainsi à un champ d’instance ou lire ainsi la longueur d’un tableau lève une NullPointerException à l’exécution. Par exemple :
String name = null;
System.out.println(name.length());
Lis le numéro de ligne de l’exception, puis identifie quelle référence manquait sur cette ligne. Les messages explicites sont activés par défaut depuis Java 15. Un message peut indiquer Cannot invoke "SimpleDate.getYear()" because "this.birthday" is null. Les noms locaux peuvent apparaître sous forme de repères comme <local4>, selon les détails de la compilation. Remonte jusqu’au constructeur, à l’affectation ou à la recherche qui aurait dû fournir l’objet.
Afficher une variable String dont la valeur est null affiche « null » ; la concaténer produit un texte comme « Hi null ». Ces opérations n’ont pas besoin d’appeler une méthode d’instance par l’intermédiaire de cette variable. Elles peuvent révéler des données manquantes avant l’échec d’un appel de méthode ultérieur.
Vérifie avant d’utiliser
L’opérateur && utilise une évaluation en court-circuit : il n’évalue le côté droit que si le côté gauche est vrai.
if (name != null && name.length() > 0) {
System.out.println(name);
}
Lorsque name vaut null, le premier test est faux et length() n’est pas exécuté. Inverser les deux tests supprime cette protection : l’appel de méthode serait exécuté en premier.
| Objectif | Expression sans risque |
|---|---|
| Vérifier l’absence | name == null |
| Vérifier la présence | name != null |
| Reconnaître une commande fixe | "quit".equals(command) |
La chaîne fixe est un objet connu : son equals peut donc recevoir null et renvoyer faux. command.equals("quit") échoue lorsque command vaut null. De même, x.equals(null) ne permet pas de vérifier si x existe : cet appel nécessite déjà un objet.
Fais de l’absence un choix de conception explicite
Initialise un champ de liste avec new ArrayList<>() afin qu’une collection vide soit immédiatement disponible. Si une recherche renvoie null pour signifier « introuvable », documente ce résultat et vérifie-le à l’endroit de l’appel. Évite de remplir chaque champ avec des objets arbitraires simplement pour faire disparaître une exception : fournis l’objet dont le programme a besoin ou donne à l’absence un sens clair que le code appelant prend en charge.
Points clés
nullsignifie qu’il n’y a aucun objet ; une chaîne vide est un objet utilisable.- Les champs reçoivent des valeurs par défaut, mais les variables locales doivent recevoir une valeur avant d’être lues.
- Déréférencer une référence nulle ou effectuer l’unboxing d’un objet enveloppe nul échoue à l’exécution.
- Place le test de null avant l’accès au membre dans
&&. - Pour comparer un texte à une chaîne fixe, appelle la méthode sur la chaîne connue.
- Initialise les collections et gère volontairement les résultats de recherche absents.
2Utilise des objets dans les appels de méthodes
Les méthodes permettent aux objets de collaborer. Un appel peut lire ou modifier un objet partagé, créer un résultat indépendant ou renvoyer un objet déjà détenu ailleurs. Ces choix influent sur ce que l’appelant voit après l’appel. Suis explicitement les références des paramètres et des valeurs de retour, et place les comparaisons dans la classe qui possède les données. Décide ensuite quelles méthodes doivent partager un état existant et lesquelles doivent le protéger par une copie.
2.1 Les paramètres reçoivent des valeurs copiées
Java transmet chaque argument par valeur. Un paramètre est une nouvelle variable qui reçoit une copie de la valeur de l’argument. Pour un argument objet, la valeur copiée est une référence.
Modifier le paramètre ou modifier l’objet ?
Un paramètre de type primitif possède sa propre donnée :
static void addTen(int n) {
n = n + 10;
}
Si x vaut 5, l’appel à addTen(x) laisse x à 5. Seul le paramètre local n prend la valeur 15.
Un paramètre de type référence peut donner accès au même objet que la variable de l’appelant. Supposons que Person.growOlder() augmente l’âge enregistré :
static void celebrate(Person p) {
p.growOlder();
}
static void replace(Person p) {
p = new Person("Bo", 30);
}
Pendant celebrate(ana), le paramètre p et la variable ana sont des alias. L’âge modifié reste visible ensuite. Pendant replace(ana), l’affectation à p ne redirige que ce paramètre. La variable ana de l’appelant référence toujours la personne initiale. Une méthode qui échange deux paramètres de type référence connaît la même limite : elle échange ses propres copies, et non les variables de l’appelant.
| Dans une méthode | Ce que constate l’appelant |
|---|---|
| Affecter une valeur à un paramètre primitif | Aucun changement de la variable de l’appelant |
| Modifier l’objet du paramètre | État modifié de l’objet partagé |
| Réaffecter un paramètre de type référence | Aucun changement de la variable de l’appelant |
Recevoir un objet d’une autre classe
Un dispositif d’accès peut lire la taille d’une personne et tenir son propre compte des contrôles. Cette classe attend une personne non nulle dont la méthode getHeight() renvoie des centimètres :
class HeightGate {
private int minHeight;
private int checks;
HeightGate(int minHeight) {
this.minHeight = minHeight;
}
boolean allowIn(Person p) {
checks++;
return p.getHeight() >= minHeight;
}
int checks() { return checks; }
}
Pour un dispositif réglé à 120 cm, contrôler des personnes de 132 cm et de 115 cm renvoie respectivement vrai et faux. Le dispositif indique ensuite 2 contrôles. Il lit la taille de la personne par une méthode publique et modifie son propre compteur.
Recevoir un autre objet de la même classe
Dans SimpleDate.before(SimpleDate other), this est la date qui reçoit l’appel et other est l’argument. Une classe peut lire les champs privés de tout objet de cette même classe.
Pour les champs day, month et year, compare dans l’ordre :
boolean before(SimpleDate other) {
if (year != other.year) {
return year < other.year;
}
if (month != other.month) {
return month < other.month;
}
return day < other.day;
}
Pour le 14 mars 2001 et le 2 novembre 2001, les années sont égales et le mois 3 est inférieur au mois 11 : la première date est donc antérieure. La comparaison de dates égales renvoie faux, car aucune ne précède l’autre.
Person.olderThan(other) peut déléguer à birthday.before(other.birthday). La comparaison des dates appartient à la classe qui possède les données des dates. Les méthodes qui appellent des méthodes d’instance sur un paramètre ont besoin d’un argument non nul ; vérifie-le ou indique cette exigence dans la documentation de la méthode.
Points clés
- Chaque paramètre reçoit une valeur copiée, y compris les paramètres de type référence.
- Une mutation peut affecter un objet partagé ; la réaffectation d’un paramètre ne peut pas remplacer la variable de l’appelant.
thisdésigne l’objet receveur ; un paramètre objet désigne l’argument.- Une classe peut accéder aux champs privés d’un autre objet de la même classe.
- Délègue les comparaisons à la classe qui possède les données concernées.
2.2 Renvoyer un nouvel objet ou un objet existant
Une méthode qui renvoie un objet renvoie une référence. Son type indique quel genre d’objet peut être renvoyé, mais pas si cet objet est nouveau, partagé ou absent. Le rôle de la méthode doit rendre ce choix clair.
Créer un résultat
Une méthode de fabrique fournit un objet. Elle peut en créer un ou réutiliser un objet existant. Ici, chaque appel crée délibérément un ticket distinct :
class TicketMachine {
private int nextNumber = 1;
Ticket issue() {
Ticket t = new Ticket(nextNumber);
nextNumber++;
return t;
}
}
La machine attend un constructeur Ticket qui stocke son numéro. Trois appels créent trois tickets numérotés 1, 2 et 3. Renvoyer une variable locale est sans risque : sa référence est copiée vers l’appelant, qui peut maintenir l’objet accessible après la sortie de portée de la variable locale.
Une méthode de copie promet un objet indépendant. Dans la classe Counter :
Counter copy() {
return new Counter(this.value);
}
Après Counter a = new Counter(4); Counter b = a.copy(); b.increase();, a.value() vaut 4 et b.value() vaut 5. Remplacer a.copy() par a partagerait le compteur.
Une méthode peut aussi construire un objet calculé. Pour un Point avec les champs double x et y :
Point midpoint(Point other) {
return new Point((x + other.x) / 2.0,
(y + other.y) / 2.0);
}
Les points (2 ; 4) et (6 ; 10) donnent le point (4,0 ; 7,0). La méthode calcule chaque coordonnée et transmet les deux à un nouveau point ; aucun des points initiaux ne change.
Renvoyer un objet déjà stocké
La méthode findByName d’un registre devrait renvoyer la personne stockée correspondante si les appelants doivent travailler avec cette entrée. Créer new Person(name, 0) renverrait des données de remplacement inventées et empêcherait les modifications ultérieures d’atteindre le registre.
Une recherche peut renvoyer null lorsqu’il n’existe aucune correspondance :
Person p = register.findByName("Lin");
if (p != null) {
System.out.println(p);
} else {
System.out.println("Not found");
}
Si p est la personne stockée, les modifications effectuées par p modifient aussi la personne à laquelle le registre donne accès. Une instruction return ne copie pas l’objet.
Garde le contrôle de l’état interne mutable
Supposons que Person.getBirthday() renvoie son champ SimpleDate et que cette date possède des mutateurs. Un appelant peut alors modifier la date de naissance de la personne sans appeler de méthode de Person. C’est une exposition de l’état mutable.
Pour protéger le champ, renvoie une nouvelle date construite à partir de ses valeurs :
SimpleDate getBirthday() {
return new SimpleDate(birthday.getDay(),
birthday.getMonth(),
birthday.getYear());
}
Cette version exige qu’un objet représentant la date de naissance ait été initialisé. D’autres conceptions utilisent une date immuable ou proposent des requêtes spécifiques sans accesseur de date. Choisis le comportement de chaque méthode : find partage normalement une entrée stockée, tandis que copy crée un objet indépendant. Le nom court d’un accesseur ne garantit pas à lui seul une protection.
Points clés
- Renvoyer un objet transmet une référence à l’appelant.
- Les méthodes de fabrique peuvent créer ou réutiliser des objets ; une méthode de copie crée un objet distinct.
- Les méthodes de recherche peuvent partager des entrées existantes et doivent définir le résultat en cas d’absence.
- Renvoyer un champ mutable peut permettre aux appelants de contourner les méthodes de l’objet propriétaire.
3Relie des objets et des listes
Un modèle plus vaste est un réseau d’objets : une personne possède une date, une liste de lecture possède une liste, et cette liste contient des morceaux. Chaque lien est une référence qui a des conséquences sur le partage et les modifications. Construis ces liens délibérément, délègue le travail à l’objet qui sait l’effectuer et protège les règles de la collection derrière des méthodes utiles. Une copie du conteneur peut protéger l’appartenance à la collection tout en partageant chacun des objets qu’elle contient.
3.1 Objets avec des champs de type référence
La composition construit une classe à l’aide de champs dont les types sont d’autres classes. Elle exprime une relation de possession : une personne a une date de naissance ; un cours a un enseignant. L’objet englobant stocke une référence à l’objet contenu.
Crée et relie les objets
Un constructeur peut recevoir une date existante :
class Person {
private String name;
private SimpleDate birthday;
Person(String name, SimpleDate birthday) {
this.name = name;
this.birthday = birthday;
}
boolean olderThan(Person other) {
return birthday.before(other.birthday);
}
public String toString() {
return name + ", born " + birthday;
}
}
L’appel new Person("Ana", new SimpleDate(14, 3, 2001)) crée d’abord une date, puis donne sa référence à la personne. Autre possibilité : un constructeur de personne peut recevoir le jour, le mois et l’année et créer la date dans son corps. Avec cette approche, l’appelant ne conserve pas de référence à cette date nouvellement créée.
Délègue le travail à l’objet contenu
L’appel birthday.before(other.birthday) demande à l’objet date de comparer les dates. Person n’a pas besoin de répéter les comparaisons des années, des mois et des jours. La concaténation dans toString() délègue elle aussi : Java appelle le toString() de l’objet date de naissance pour obtenir son texte.
Un objet date peut aider à calculer l’âge. Commence par soustraire l’année de naissance de l’année en cours. Construis ensuite la date de l’anniversaire de cette année et soustrais un si la date actuelle la précède. Une personne née le 14 mars 2001 a 24 ans le 1er janvier 2026 : la différence entre les années est de 25, mais l’anniversaire n’est pas arrivé. Le 14 mars 2026, l’anniversaire est arrivé et l’âge est de 25 ans. Garder la comparaison des dates dans SimpleDate évite de reproduire ses règles dans chaque méthode de personne.
Décide si l’objet contenu est partagé
Le constructeur ci-dessus stocke directement son argument. Si deux personnes reçoivent une même SimpleDate mutable, les deux champs de date de naissance pointent vers la même date. Modifier cette date affecte les deux personnes.
| Choix du constructeur | Conséquence pour les références |
|---|---|
| Stocker la référence à la date reçue | L’appelant et la personne peuvent partager la date |
| Construire une nouvelle date à partir des valeurs reçues | La personne commence avec sa propre date |
| Stocker une date immuable | Le partage ne peut pas modifier les valeurs de la date |
Un constructeur défensif peut copier le jour, le mois et l’année de la date reçue. Une date immuable n’a pas de méthode qui modifie son état et garde ses valeurs fixes ; des champs primitifs simples peuvent être final. Copier à l’entrée est important même si un accesseur renvoie ensuite une copie, car l’appelant initial peut encore détenir l’objet reçu.
Gère les liens facultatifs
Un Course peut ne pas encore avoir d’enseignant. Si cet état a un sens, teacherName() peut vérifier le champ et renvoyer « pas encore d’enseignant ». Sinon, exige un enseignant à la construction.
Dans course.getTeacher().getName(), chaque appel d’instance nécessite un objet : si le cours est nul, l’appel à getTeacher() échoue ; si l’enseignant est nul, c’est l’appel à getName() qui échoue. Une méthode du cours peut garder ce test d’absence à un seul endroit et éviter que chaque appelant ait besoin de connaître la même chaîne.
Points clés
- La composition relie un objet englobant à d’autres objets par des champs.
- Délègue les opérations à l’objet qui possède les données concernées.
- Stocker une référence mutable reçue peut partager l’état avec l’appelant.
- Des copies ou des objets contenus immuables peuvent protéger un état indépendant.
- Les champs d’objet facultatifs nécessitent une règle claire de gestion de null.
3.2 Les listes contiennent des références à leurs éléments
Une ArrayList<Person> est un objet dont les éléments contiennent des références à des personnes. La liste contrôle la succession de ses entrées ; chaque personne contrôle ses propres champs. Ce sont deux types de modifications distincts.
Ajoute un objet et accède-y à nouveau
ArrayList<Person> people = new ArrayList<>();
people.add(ana);
people.get(0).growOlder();
Ce code nécessite import java.util.ArrayList; et une personne ana existante et non nulle. add copie la référence. L’appel à growOlder() accède à la même personne que ana : la modification de l’âge est donc visible par les deux chemins. Ici, la liste ne crée jamais de deuxième personne.
Tu peux créer un objet au moment de l’ajouter : people.add(new Person("Lin", 1998));, à l’aide d’un constructeur de personne qui accepte un nom et une année de naissance. La liste donne alors au programme un chemin vers cette nouvelle personne par get ou par une boucle. Les arguments de type d’une liste doivent être des types référence : utilise donc ArrayList<Integer> pour les entiers.
Calcule à partir d’objets et sélectionne un objet
Supposons qu’une liste contienne des colis de 2,5 kg, 4,0 kg et 1,5 kg. Une boucle additionne le poids de chaque colis :
double total = 0;
for (Parcel p : parcels) {
total += p.getWeight();
}
Les sommes cumulées sont 2,5, 6,5 et 8,0 kg. Pour sélectionner le colis le plus lourd, il faut une référence et une comparaison :
Parcel heaviest = parcels.get(0);
for (Parcel p : parcels) {
if (p.getWeight() > heaviest.getWeight()) {
heaviest = p;
}
}
Cette sélection suppose que la liste n’est pas vide. Le résultat est l’objet colis de 4,0 kg : l’appelant peut donc aussi utiliser ses autres méthodes. Pour une liste vide, décide du résultat à renvoyer en cas d’absence avant de lire l’élément 0.
Parcours toutes les entrées lors d’une recherche
Une recherche par nom dans une classe qui stocke people peut utiliser :
Person findByName(String name) {
for (Person p : people) {
if (p.getName().equals(name)) {
return p;
}
}
return null;
}
Cette version attend des personnes non nulles dont les noms sont non nuls. La première correspondance est renvoyée immédiatement. Le return null final vient après la boucle : une comparaison infructueuse sur la première personne n’exclut pas des correspondances ultérieures.
contains, indexOf et remove(Object) effectuent leurs recherches avec equals. Si Person hérite d’une égalité fondée sur l’identité, une personne nouvellement construite ne correspondra pas à une personne existante simplement parce que leurs champs correspondent.
Retire une entrée sans détruire l’objet
Un objet peut apparaître deux fois dans une liste ou dans plusieurs listes. Retirer une entrée ne détruit pas l’objet et ne supprime pas les autres références vers lui.
Ne retire pas directement des entrées de la même liste pendant un parcours for-each. Cela peut déclencher une ConcurrentModificationException. Recueille d’abord les candidats, puis retire-les :
ArrayList<Person> toRemove = new ArrayList<>();
for (Person p : people) {
if (p.getName().equals("Lin")) {
toRemove.add(p);
}
}
people.removeAll(toRemove);
Le parcours se termine avant que le contenu de la liste change. removeAll utilise l’égalité : elle retire donc les entrées égales aux candidats recueillis.
Points clés
- Ajouter un objet à une liste partage une référence à cet objet.
- Une boucle peut calculer une somme ou conserver une référence à un objet sélectionné.
- Une recherche infructueuse ne se termine qu’après la vérification de tous les candidats.
- Les opérations d’appartenance à une liste dépendent de
equals. - Retirer une entrée laisse les autres références intactes.
- Recueille d’abord les éléments à supprimer et modifie la liste après le parcours.
3.3 Une classe qui possède une liste
Une classe peut conserver une liste dans un champ privé et proposer des opérations qui décrivent son rôle. Une liste de lecture stocke des morceaux, les compte et calcule leur durée. Les appelants ne devraient pas avoir à gérer directement sa liste.
Initialise et encapsule la collection
Cette liste de lecture attend des objets Song non nuls avec une méthode seconds() :
import java.util.ArrayList;
class Playlist {
private String name;
private ArrayList<Song> songs;
Playlist(String name) {
this.name = name;
this.songs = new ArrayList<>();
}
void add(Song song) { songs.add(song); }
int count() { return songs.size(); }
int totalSeconds() {
int total = 0;
for (Song song : songs) {
total += song.seconds();
}
return total;
}
}
Sans new ArrayList<>(), le champ de type référence reste à null ; le premier appel à add échoue alors. Une liste vide est déjà un objet utilisable, de taille 0.
Pour des morceaux de 210 s, 185 s et 247 s, count() renvoie 3. La somme passe de 210 à 395, puis à 642 s. La division entière par 60 donne 10 minutes, avec un reste de 42 secondes. Une méthode longest() peut sélectionner et renvoyer le morceau de 247 s, selon le même schéma de sélection de référence que pour le colis le plus lourd. Définis un résultat en cas d’absence pour une liste de lecture vide.
Protège le contenu de la liste par des méthodes
Garder songs privé bloque l’accès direct au champ, mais return songs; expose cette liste elle-même. Un appelant pourrait appeler clear() et contourner toutes les règles de la liste de lecture.
Des opérations spécifiques comme add, count et totalSeconds laissent la responsabilité à la liste de lecture. Sa méthode add pourrait imposer une taille maximale ou refuser les doublons avec songs.contains(song). Pour détecter les doublons par leur contenu, il faut que equals de la classe des morceaux définisse les champs qui rendent deux morceaux égaux.
Lorsqu’un constructeur reçoit une liste initiale, copie sa structure avec this.songs = new ArrayList<>(initialSongs);. Stocker directement le paramètre permettrait à l’appelant de conserver un alias vers la liste interne de la liste de lecture.
Une nouvelle liste peut toujours partager les morceaux
Une requête peut renvoyer new ArrayList<>(songs). C’est une copie superficielle : un nouvel objet liste qui contient les références aux morceaux existants.
| Action de l’appelant sur la copie renvoyée | Effet sur la liste de lecture |
|---|---|
| Retirer une entrée | Le contenu de la liste de lecture reste inchangé |
| Vider la copie | Le contenu de la liste de lecture reste inchangé |
| Modifier un morceau partagé | La liste de lecture voit l’état modifié de ce morceau |
Une requête songsLongerThan(200) crée une nouvelle liste de résultats, parcourt les morceaux stockés et ajoute ceux dont la durée dépasse 200 s. Pour les trois morceaux ci-dessus, elle renvoie ceux de 210 s et de 247 s. Lorsque les valeurs des morceaux doivent aussi être indépendantes, copie les morceaux mutables ou rends Song immuable.
Points clés
- Un objet propriétaire, sa liste et ses éléments sont des objets distincts.
- Initialise la liste avant de l’utiliser.
- Propose des opérations qui imposent les règles de l’objet propriétaire pour sa collection.
- Copie les listes reçues pour empêcher un alias extérieur vers la liste interne.
- Une copie superficielle de liste partage les objets qui en sont les éléments.
4Définis ce qui rend des objets égaux
Deux objets peuvent décrire la même chose tout en restant distincts. Java propose différents tests pour l’identité et le contenu, et les collections dépendent du choix effectué par ta classe. Commence par choisir les champs qui définissent l’égalité, puis implémente ensemble equals et hashCode. Leurs contrats expliquent pourquoi une liste peut trouver un objet égal, pourquoi une collision de hachage est autorisée et pourquoi modifier une clé stockée peut compromettre une recherche.
4.1 Identité et contenu égal
L’identité indique si deux références mènent au même objet. L’égalité de contenu indique si des objets ont les mêmes données pertinentes, selon les règles de leur classe. Deux objets livre distincts peuvent décrire le même titre et la même année de publication sans être identiques.
Choisis la bonne comparaison
| Valeurs comparées | Signification de == | Signification de equals |
|---|---|---|
| Valeurs primitives | Compare les valeurs primitives | Aucune méthode d’instance sur une valeur primitive |
| Références | Même objet, ou les deux à null | Règle d’égalité de la classe |
| Chaînes | Même objet chaîne | Contenu en caractères identique |
Classe héritant de Object.equals | Même objet | Même objet |
Chaque classe hérite de equals(Object) de Object. Cette méthode par défaut vérifie l’identité. Java n’inspecte pas automatiquement tous les champs d’une nouvelle classe.
String a = "java";
String b = new String("java");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
new String crée un objet distinct, tandis que String.equals compare ses caractères. La comparaison de texte est sensible à la casse : "Java".equals("java") vaut faux. equalsIgnoreCase permet une comparaison sans tenir compte de la casse lorsque c’est la règle voulue.
Pourquoi l’identité semble parfois comparer le contenu
Des littéraux de chaîne égaux partagent un objet chaîne : "java" == "java" vaut donc vrai. Cela peut masquer un bogue dans un petit test. Un texte obtenu par saisie ou construit pendant l’exécution d’un programme ne partage pas nécessairement cet objet. Utilise equals pour le contenu, quelle que soit la façon dont le texte a été obtenu.
Les comparaisons d’objets enveloppes présentent un piège similaire :
Integer a = 127, b = 127;
Integer c = 1000, d = 1000;
System.out.println(a == b); // true
System.out.println(c.equals(d)); // true
L’autoboxing réutilise des objets Integer pour les valeurs de −128 à 127. Le fait que des valeurs plus grandes partagent ou non un objet ne constitue pas une règle de comparaison numérique portable. Dans une configuration standard, les deux objets enveloppes de valeur 1 000 sont distincts, mais un cache plus grand est autorisé. Utilise equals sur les objets enveloppes pour comparer leurs valeurs, ou compare des valeurs primitives. Si un côté de == est un int, Java effectue l’unboxing de l’objet enveloppe et compare les valeurs numériques ; l’unboxing d’un objet enveloppe nul échoue.
Décide quels champs définissent l’égalité
Pour un catalogue de livres, le titre et l’année de publication pourraient définir l’égalité, tandis que l’emprunteur actuel serait ignoré. Ces champs sélectionnés doivent déterminer le fonctionnement de la méthode d’égalité. Un autre programme pourrait choisir un identifiant unique à la place ; la règle utile dépend de ce que représentent ses objets.
Une ArrayList utilise equals dans contains, indexOf et remove(Object). Supposons que Book se contente de stocker des champs et hérite de equals. Ajouter un new Book("Dune", 1965) ne rend pas contains(new Book("Dune", 1965)) vrai : la seconde expression crée un autre objet. Redéfinir l’égalité selon le titre et l’année permet à une recherche de trouver ce contenu égal.
HashSet et HashMap utilisent aussi hashCode pour réduire le nombre d’objets candidats avant de vérifier l’égalité. Définir une égalité de contenu nécessite donc aussi une règle de hachage cohérente.
Points clés
- Pour les références,
==teste l’identité, tandis queequalssuit la règle de la classe. Object.equalscompare l’identité jusqu’à sa redéfinition.- Utilise
String.equalspour comparer le contenu textuel. - Les littéraux partagés et la mise en cache des objets enveloppes peuvent rendre les tests d’identité trompeurs.
- Choisis délibérément les champs d’égalité ; les collections dépendent de ce choix.
4.2 Implémenter correctement equals
Une méthode d’égalité doit remplacer la méthode equals(Object) héritée et comparer les champs qui définissent le sens de ton objet. La bonne signature compte autant que les comparaisons qu’elle contient.
Un schéma complet pour l’égalité
Cette classe utilise le titre et l’année, accepte un titre nul et garde ses champs d’égalité fixes :
import java.util.Objects;
final class Book {
private final String title;
private final int year;
Book(String title, int year) {
this.title = title;
this.year = year;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Book)) {
return false;
}
Book book = (Book) other;
return year == book.year
&& Objects.equals(title, book.title);
}
@Override
public int hashCode() {
return Objects.hash(title, year);
}
}
Lis equals en quatre étapes. Le test d’identité accepte immédiatement le même objet. Le test de type rejette tout ce qui n’est pas un livre, y compris null. Le transtypage fournit une variable Book par laquelle les champs peuvent être lus. L’expression finale vérifie les deux champs d’égalité.
Le type du paramètre est Object, et non Book. Écrire equals(Book other) surcharge le nom avec une autre méthode et laisse la méthode héritée equals(Object) inchangée. Un appel direct peut alors sembler fonctionner tandis que ArrayList.contains utilise toujours l’identité. Grâce à @Override, le compilateur détecte cette erreur de signature.
À partir de Java 16, le test de type et le transtypage peuvent être combinés : if (!(other instanceof Book book)) { return false; }. Après un test réussi, book est disponible pour comparer les champs.
Adapte les comparaisons aux types des champs
Utilise == pour les champs entiers, caractères et booléens. Utilise equals d’un objet pour comparer son contenu. Pour un champ objet qui peut être nul, Objects.equals(a, b) renvoie vrai si les deux valeurs sont nulles, faux si une seule est nulle, et utilise sinon l’égalité.
Pour les champs double, Double.compare(x, other.x) == 0 traite les valeurs spéciales de façon cohérente : deux NaN sont considérés comme égaux, et les zéros positif et négatif sont distingués. Associe ce choix à une règle de hachage compatible. L’opérateur == appliqué directement aux nombres à virgule flottante traite NaN différemment.
Vérifie le contrat d’égalité
Pour les références non nulles, l’égalité doit être :
- Réflexive :
x.equals(x)vaut vrai. - Symétrique :
x.equals(y)ety.equals(x)donnent le même résultat. - Transitive : si
xest égal àyetyàz, alorsxest égal àz. - Cohérente : les appels répétés donnent le même résultat tant que les données comparées restent inchangées.
- Fausse pour null :
x.equals(null)vaut faux.
Deux livres pour « Dune », 1965, sont considérés comme égaux ; remplacer l’une des années par 1984 les rend inégaux. Un livre ne doit pas être considéré comme égal à une chaîne contenant son titre : la chaîne rejetterait le livre, ce qui romprait la symétrie.
Certaines implémentations utilisent other == null || getClass() != other.getClass() pour le test de type. Ce test rejette les classes effectives différentes. instanceof peut accepter les sous-classes ; pour la classe finale ci-dessus, aucune sous-classe n’existe et les deux tests donnent le même résultat.
Points clés
- Redéfinis la méthode avec
public boolean equals(Object other)et@Override. - Vérifie l’identité et le type, convertis la référence et compare les champs d’égalité.
- Utilise des comparaisons d’objets qui gèrent null lorsque des champs peuvent être absents.
- Respecte la réflexivité, la symétrie, la transitivité, la cohérence et le rejet de null.
equals(Book)surcharge la méthode et ne remplace pasequals(Object).- Le choix du test de type compte lorsque les sous-classes sont autorisées.
4.3 Codes de hachage cohérents et clés stables
Un code de hachage est un entier utilisé pour réduire le champ d’une recherche. HashSet et HashMap l’utilisent pour trouver des objets candidats, puis utilisent equals pour confirmer une correspondance. Ce n’est pas un identifiant unique d’objet.
Le contrat fonctionne dans un seul sens
Des objets égaux doivent renvoyer le même code de hachage. Des objets inégaux peuvent aussi partager un code ; c’est une collision. Une collision ne les rend pas égaux, car l’égalité est toujours vérifiée.
Un code de hachage doit rester cohérent tant que les informations utilisées par equals restent inchangées. Avec une implémentation correcte, des codes de hachage différents excluent l’égalité, mais des codes égaux ne la prouvent pas.
Lorsqu’une classe redéfinit equals mais hérite de Object.hashCode, des objets égaux distincts reçoivent souvent des codes différents. Un ensemble peut alors conserver les deux, et une recherche dans une Map avec une nouvelle clé égale peut échouer. Cet échec précis n’est pas garanti, car les codes hérités d’objets différents peuvent entrer en collision. Corrige le contrat au lieu de compter sur l’échec d’une recherche particulière.
Calcule à partir des champs d’égalité
Pour la classe de livre qui compare le titre et l’année :
@Override
public int hashCode() {
return Objects.hash(title, year);
}
Importe java.util.Objects. Les mêmes valeurs de champs produisent maintenant le même résultat. Un ensemble de livres conserve une entrée après l’ajout de deux livres égaux, et une Map peut retrouver une valeur à l’aide d’une nouvelle clé livre égale.
Objects.hash combine les codes de hachage des champs à l’aide de 31. Pour « Dune », le code de hachage de la chaîne est 2 141 736. La combinaison de ce titre et de l’année 1965 donne 31 multiplié par la somme de 31 et de 2 141 736, puis ajoute 1965 : le résultat est 66 396 742.
Le hachage des chaînes utilise aussi 31. Pour « ab », les codes de caractères 97 et 98 donnent 97 multiplié par 31 plus 98, soit 3 105. Des calculs plus longs peuvent dépasser la plage de int et produire un retour par dépassement de capacité : un code de hachage négatif est donc normal.
| Règle de hachage | Conséquence pour le contrat |
|---|---|
| Utiliser les mêmes champs d’égalité | Les objets égaux reçoivent des codes égaux |
| Ajouter un champ ignoré par l’égalité | Les objets égaux peuvent recevoir des codes différents |
| Utiliser moins de champs d’égalité | Autorisé, mais peut créer plus de collisions |
| Toujours renvoyer 1 | Autorisé, mais donne de mauvaises performances de recherche |
Ne modifie pas la clé pendant son stockage
Supposons que l’année d’un livre soit mutable et influe sur son code de hachage. Après son insertion dans un ensemble, modifier l’année peut faire échouer contains(book) même avec cette même référence : la recherche utilise désormais un autre code. L’ensemble peut toujours indiquer une taille de 1.
Garde les champs d’égalité stables tant qu’un objet est un élément d’ensemble ou une clé de Map. S’ils doivent changer, retire l’objet tant que ses anciennes valeurs s’appliquent encore, modifie-le et insère-le à nouveau. Tenter de le retirer seulement après la modification peut entraîner le même problème de recherche.
Une égalité générée exige tout de même un bon choix de données
Java 16 et les versions ultérieures prennent en charge les records, comme record Book(String title, int year) {}. Un record génère les accesseurs title() et year(), ainsi que l’égalité, le hachage et une représentation lisible sous forme de chaîne à partir de tous ses composants. Les champs des composants sont final. Cet exemple constitue une valeur immuable utile, car ses composants sont une chaîne et un entier ; un record contenant une liste mutable partagerait toujours l’état de cette liste.
Pour les classes ordinaires, un IDE peut générer ensemble equals et hashCode. Sélectionne les champs qui définissent l’égalité voulue et garde les deux méthodes cohérentes lorsque ce choix change.
Points clés
- Des objets égaux doivent avoir des codes de hachage égaux ; les collisions entre objets inégaux sont autorisées.
- Redéfinis
hashCodechaque fois que tu redéfinisequals. - Ne hache que des données compatibles avec la règle d’égalité.
- Garde stables les champs utilisés pour le hachage tant que l’objet appartient à un ensemble ou sert de clé de Map.
- Retire une clé avant de la modifier, puis insère-la à nouveau.
- Les records génèrent l’égalité à partir de leurs composants ; les composants mutables demandent toujours de l’attention.
5Attribue une tâche précise à chaque classe
Un programme textuel effectue deux types de travail : communiquer avec un utilisateur et gérer les données et les règles. Placer ces tâches dans des classes distinctes facilite la vérification et la réutilisation des règles. Relie les classes par des paramètres de constructeur et de petites méthodes publiques. Ajoute ensuite une fonctionnalité à l’endroit où sa règle doit se trouver, vérifie le résultat avec des valeurs connues et donne à l’interface une commande qui l’appelle et affiche la réponse.
5.1 Entrées, sorties et règles du programme
L’interface utilisateur lit les entrées et affiche les sorties. La logique applicative stocke les données et applique les règles. Dans une conception séparée, des valeurs sont transmises à la logique, qui renvoie des résultats ; les classes de logique n’utilisent ni Scanner ni System.out.
Relie les objets dans main
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
TemperatureLog log = new TemperatureLog();
UserInterface ui = new UserInterface(scanner, log);
ui.start();
}
Avec import java.util.Scanner;, cet assemblage crée un lecteur d’entrée, un journal et une interface. Il appartient à la classe qui contient le point d’entrée du programme. UserInterface stocke les objets reçus dans des champs afin que chaque commande utilise le même journal.
| Travail | Classe responsable |
|---|---|
| Lire le texte d’une commande | Interface utilisateur |
| Stocker un relevé | Journal de températures |
| Calculer la moyenne ou le maximum | Journal de températures |
| Arrondir et afficher un résultat | Interface utilisateur |
| Rejeter une température impossible | Journal de températures |
| Expliquer à l’utilisateur qu’une entrée est incorrecte | Interface utilisateur |
Renvoie des valeurs que tout appelant peut utiliser
Un journal peut proposer add(double celsius), count(), average() et max(). Les relevés 18,5, 21,0 et 19,5 °C ont pour somme 59,0, pour effectif 3, pour maximum 21,0 et pour moyenne environ 19,6667 °C. L’interface peut arrondir la moyenne renvoyée avec Math.round(average * 10) / 10.0 et afficher 19,7 °C.
Renvoyer le nombre facilite les vérifications directes. Une méthode nommée printAverage() qui se contente d’afficher lie un calcul à la sortie sur la console, ce qui rend sa réutilisation moins pratique.
Pour une boucle de commande simple, supposons que add accepte les températures valides et que count renvoie leur nombre :
class UserInterface {
private Scanner scanner;
private TemperatureLog log;
UserInterface(Scanner scanner, TemperatureLog log) {
this.scanner = scanner;
this.log = log;
}
void start() {
while (true) {
String command = scanner.nextLine();
if ("quit".equals(command)) {
break;
}
if ("count".equals(command)) {
System.out.println(log.count());
}
}
}
}
La boucle gère l’interaction. Elle demande un résultat au journal et l’affiche sans savoir comment les relevés sont stockés. Une commande de moyenne devrait gérer un journal vide selon le résultat documenté de la logique ou vérifier count() avant d’appeler une méthode qui exige des relevés.
Valide à la bonne limite
La logique peut faire renvoyer faux à add sans stocker un relevé inférieur à −273,15 °C. Chaque appelant bénéficie alors de la même règle de validation des données. L’interface décide comment signaler ce rejet et demander une nouvelle saisie. Transformer une entrée non numérique en message d’erreur utile relève aussi de l’interface.
Utilise un seul Scanner pour System.in et transmets-le aux lecteurs. Plusieurs scanners peuvent se disputer les données d’entrée mises en tampon, et en fermer un ferme le flux d’entrée sous-jacent. Confie la durée de vie du scanner partagé au code responsable des entrées du programme.
La logique peut ensuite prendre en charge une autre interface par les mêmes méthodes publiques. La réutilisation est possible lorsque les calculs et les règles de validité sont accessibles sous forme de résultats de méthodes, indépendamment d’une boucle de commande particulière.
Points clés
- L’interface lit et affiche ; la logique stocke les données, impose les règles et renvoie les résultats.
mainconstruit les objets partagés et démarre l’interface.- Transmets un scanner et l’objet de logique au constructeur de l’interface.
- Les règles de validation des données appartiennent à la logique ; les messages de saisie et le formatage de l’affichage appartiennent à l’interface.
- Renvoyer des résultats facilite les vérifications directes et la réutilisation avec une autre interface.
5.2 Ajoute une fonctionnalité et vérifie son comportement
Un programme séparé peut évoluer par petites modifications successives. Donne à la logique une méthode utile, vérifie-la directement, puis relie-la à une commande qui recueille ses entrées et affiche son résultat.
Ajoute une requête à la logique
Un TemperatureLog qui stocke une ArrayList<Double> readings initialisée peut compter les relevés supérieurs à un seuil :
int countAbove(double limit) {
int count = 0;
for (double reading : readings) {
if (reading > limit) {
count++;
}
}
return count;
}
La comparaison stricte exclut les relevés exactement égaux au seuil. Pour 18,5, 21,0, 19,5 et 23,0 °C, countAbove(20.0) renvoie 2. Place la comparaison dans le journal afin que chaque appelant bénéficie de la même règle.
La commande de l’interface lit un seuil, appelle log.countAbove(limit) et affiche le nombre obtenu. Si chaque commande devient longue, décompose start() en méthodes auxiliaires privées comme addReading() et showStatistics(). Ces méthodes organisent l’interaction ; elles ne déplacent pas les règles de validation des données hors du journal.
Vérifie sans saisir de commandes
Crée l’objet de logique et appelle ses méthodes avec des valeurs dont tu connais les résultats :
TemperatureLog log = new TemperatureLog();
log.add(18.5);
log.add(21.0);
log.add(19.5);
log.add(23.0);
System.out.println(log.count() == 4);
System.out.println(log.average() == 20.5);
System.out.println(log.max() == 23.0);
System.out.println(log.countAbove(20.0) == 2);
La somme est de 82,0, donc la moyenne attendue est de 20,5. Ces valeurs particulières ont des représentations binaires exactes, ce qui rend ces tests directs d’égalité adaptés à l’exemple. Vérifie aussi un journal vide selon son comportement documenté, un seuil égal à un relevé et le rejet d’une entrée incorrecte sans modification du nombre de relevés. Les outils de test automatisés reposent sur la même idée : une entrée connue et un résultat attendu.
Ne modifie le stockage que s’il permet toujours de répondre aux requêtes
Une interface qui n’appelle que average, count et max n’a pas besoin de savoir si le journal contient une liste. Des champs pour la somme cumulée, le nombre de relevés et le maximum peuvent répondre à ces trois requêtes sans conserver chaque relevé.
Une requête countAbove(limit) avec un seuil quelconque exige davantage d’informations. Les échantillons 18, 22, 23 et 20, 20, 23 ont la même somme de 63, le même effectif de 3 et le même maximum de 23, mais leurs nombres de valeurs supérieures à 20 sont respectivement 2 et 1. Ne conserver que ces trois agrégats fait perdre la distinction. Les méthodes publiques décrivent ce qui doit continuer à fonctionner après une modification du stockage.
Combine les idées de conception
Un livre de recettes peut posséder une liste privée de recettes. Chaque recette peut contenir des ingrédients mesurés en grammes et un temps de cuisson en minutes. L’égalité peut servir à détecter les doublons, tandis qu’une interface ajoute, affiche et recherche des recettes par les méthodes du livre. Une copie de la liste de résultats peut protéger son contenu tout en partageant les objets recette ; décide si les appelants peuvent modifier ces recettes.
Travaille par petites étapes : implémente une méthode, vérifie-la, puis ajoute sa commande. Si une modification casse un résultat existant, reviens à un état fonctionnel et isole cette modification avant d’ajouter une autre fonctionnalité.
Points clés
- Ajoute et vérifie la méthode de logique avant de relier sa commande dans l’interface.
- Utilise des entrées connues et des résultats attendus pour vérifier directement la logique.
- Inclus des cas limites comme une collection vide ou une valeur exactement égale au seuil.
- Une modification du stockage doit préserver assez d’informations pour chaque requête publique.
Références et égalité en Java : exemples
1Suis les variables sans perdre les objets
15 étapes
- 1IntroductionDessine les variables et les objets
Une variable peut contenir directement un nombre ou contenir une référence qui mène à un objet. Cette différence explique pourquoi copier une variable donne parfois des données indépendantes et partage parfois une modification.
Nous allons dessiner les variables séparément des objets, puis suivre chaque instruction. Distingue le nombre de flèches du nombre d’objets : plusieurs flèches peuvent aboutir à un même objet.
- 2ExplicationUne valeur peut être une donnée ou une référence
Java possède huit types primitifs :
byte,short,int,long,float,double,charetboolean. Leurs variables contiennent directement les données. Les classes et les tableaux sont des types référence ; leurs variables contiennent des références ounull.Dans
int count = 13;, la valeur est 13. DansTally t = new Tally(13);, la valeur detest une référence à un objet qui contient ses propres données. - 3ExempleCopie une valeur primitive
Suis les affectations dans l’ordre :
int start = 13; int next = start; next += 2;La deuxième instruction copie 13 dans
next. La troisième lit cette copie et la remplace par 15.startreste à 13, car aucune instruction ne lui affecte de nouvelle valeur. Dessine deux boîtes contenant des nombres distincts ; aucun objet partagé n’intervient. - 4ExempleCopie une valeur de type référence
Supposons que
Tallystocke un entier, queaddOne()l’augmente et quevalue()le lise.Tally first = new Tally(13); Tally next = first; next.addOne();Un
newcrée un compteur. L’affectation donne ànextun autre chemin vers celui-ci. L’appel àaddOne()porte sa valeur stockée à 14, quefirst.value()lit maintenant aussi. La seconde variable n’est pas un second compteur. - 5DéfiDistingue un objet de son alias
Avec les mêmes méthodes de
Tally, que renvoiea.value()?Tally a = new Tally(20); Tally b = new Tally(a.value()); b.addOne();---reveal---
Elle renvoie 20. Le second
newcrée un compteur indépendant initialisé à 20 ; son incrémentation ne fait passer que ce second objet à 21. Lire la valeur d’un champ et l’utiliser dans un constructeur diffère de la copie de la référence à l’objet d’origine. - 6LienCertains objets partagés ne peuvent pas être modifiés
Les chaînes et les classes enveloppes comme
Integersont des types référence. L’autoboxing convertit les valeurs primitives en objets enveloppes pour des usages commeArrayList<Integer>.Les chaînes sont immuables : si
leftetrightpartagent « cedar », l’affectationright = right.toUpperCase()ne modifie pas les caractères d’origine.leftreste « cedar ». Le résultat renvoyé en majuscules n’est affecté qu’àright. - 7AnalogieUtilise des flèches pour suivre les modifications
Imagine chaque variable comme une carte indiquant un chemin vers un objet. Copier la carte donne un autre chemin vers la même destination. Écrire un autre chemin sur une carte ne réécrit pas les autres.
Dans un diagramme de mémoire, dessine une flèche de chaque variable de type référence vers son objet. Un mutateur modifie les valeurs des champs de l’objet ; une affectation modifie la flèche d’une variable. Distingue visuellement ces deux modifications.
- 8ExempleModifie d’abord l’état partagé
Supposons que
Cratestocke une masse en kilogrammes, avec les méthodessetMassetmass.Crate a = new Crate(3); Crate b = a; b.setMass(6);Dessine
aetbpointant vers une même caisse. Le mutateur fait passer la masse dans cette caisse de 3 à 6.a.mass()etb.mass()renvoient maintenant tous deux 6 ; le mutateur n’a redirigé aucune des deux variables. - 9ExempleRedirige ensuite une variable
Poursuis le suivi de la caisse :
b = new Crate(9);Crée un second objet de masse 9 et redirige uniquement
b. La variableapointe toujours vers la caisse d’origine, dont la masse reste de 6. Il y a maintenant deux objets caisse accessibles. C’est une réaffectation : rien ne réinitialise donc la masse stockée dans la première caisse. - 10DéfiConserve le chemin restant
Continue avec
Crate c = a; a = null;. Par quelle variable peut-on encore accéder à la caisse de 6 kg, et peut-elle faire l’objet d’une récupération de mémoire ?---reveal---
Elle reste accessible par
c: elle ne peut donc pas faire l’objet d’une récupération de mémoire. L’affectation précédente a copié la référence dansc; affecter null àan’a supprimé qu’un seul chemin. La caisse distincte de 9 kg reste accessible parb. - 11Point cléUn champ ou une liste peut conserver un chemin
Lorsque le dernier chemin utilisable vers un objet disparaît, celui-ci devient inaccessible et sa mémoire peut être récupérée. Java choisit le moment de la récupération ; perdre une référence ne garantit pas une récupération immédiate.
Un chemin peut passer par un autre objet ou une liste. Avant de déclarer un objet inaccessible, vérifie ces liens ainsi que les variables locales. Une entrée de liste peut conserver un objet après la sortie de portée de sa variable d’origine.
- 12ExplicationNull signifie que la destination est absente
nulln’est pas un objet vide. UneStringnulle n’a aucun objet sur lequel appelerlength(), tandis que""est une chaîne utilisable de longueur zéro. Les champs de type référence valent null par défaut ; les variables locales doivent recevoir une valeur avant d’être lues.Les variables de type enveloppe peuvent aussi valoir null.
Integer n = null; int count = n;lève uneNullPointerExceptionlorsque l’unboxing tente d’obtenir une valeur primitive à partir de l’objet enveloppe absent. - 13ConseilLis l’erreur à l’appel qui échoue
Pour une erreur liée à null, repère le numéro de ligne, puis l’appel d’instance, l’accès au champ ou l’unboxing sur cette ligne. Les messages explicites, activés par défaut depuis Java 15, peuvent identifier le champ manquant.
Remonte jusqu’au constructeur, au résultat de recherche ou à l’affectation qui aurait dû fournir un objet. Voir « null » dans le texte affiché est un indice : afficher une chaîne nulle peut réussir même si un appel d’instance ultérieur échoue.
- 14DéfiSécurise le suivi combiné
Une recherche utilise
Crate found = findCrate();: la méthode renvoie soit une caisse, soit null. Quelle condition vérifie sans risque que la masse dépasse 5 kg ? Pourquoi ses tests doivent-ils être dans cet ordre ?---reveal---
Utilise
found != null && found.mass() > 5. Le test de null est exécuté en premier et&&sautemass()si aucune caisse n’existe. Placer l’appel de méthode en premier déréférencerait null avant d’atteindre le test. Une référence renvoyée suit les mêmes règles que les alias suivis précédemment. - 15RécapitulatifVérifie le suivi des références
Pour un nouveau suivi, repère chaque
new, copie la valeur à chaque affectation et ne mets à jour que le champ visé par une mutation. Suis ensuite tous les chemins restants avant de décider qu’un objet est inaccessible.Sur une ligne qui échoue, identifie l’opération précise qui nécessite un objet. Vérifie null avant cette opération ou initialise le champ avec l’objet dont le programme a réellement besoin, comme une liste vide.
2Suis un objet à l’entrée et à la sortie d’une méthode
12 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionSuis un appel et son résultat
- 2ExplicationUn paramètre reçoit sa propre valeur
- 3ExempleModifie l’objet partagé
- 4ExempleRéaffecte la référence locale
- 5DéfiUne méthode peut-elle échanger les variables de l’appelant ?
- 6LienCompare l’objet receveur à son argument
- 7ExplicationLe type de retour ne promet pas une copie
- 8ExempleConstruis un objet indépendant à renvoyer
- 9DéfiTrouve une entrée qui peut être mise à jour
- 10ConseilUn accesseur peut révéler un alias caché
- 11DéfiCombine une valeur de retour avec un appel prenant un paramètre
- 12RécapitulatifExamine les deux points de passage d’une méthode
3Construis et protège une collection d’objets
16 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionDistingue les niveaux d’état
- 2ExplicationModélise une relation de possession
- 3ExempleDélègue la règle de comparaison des dates
- 4DéfiProtège la date reçue
- 5ExplicationUne entrée est un autre chemin
- 6ExempleCalcule une somme avec une boucle
- 7ExempleConserve l’objet sélectionné
- 8DéfiRenvoie l’absence au bon moment
- 9ConseilSépare la suppression du parcours
- 10ExplicationDonne à l’objet propriétaire une liste initialisée
- 11ExemplePropose des opérations utiles
- 12DéfiDistingue une copie de liste de ses éléments
- 13LienProtège les deux directions
- 14ExempleFiltre dans un nouveau résultat
- 15DéfiSuis une copie à travers un autre objet
- 16RécapitulatifExamine les points de passage de la collection
4Construis une égalité fiable pour les collections
16 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionDécide ce que signifie une correspondance
- 2ExempleUn texte égal dans des objets distincts
- 3ConseilNe laisse pas la mise en cache choisir la règle
- 4DéfiDonne une égalité de contenu à une classe
- 5ExplicationChoisis les champs et la signature
- 6ExempleAccepte l’identité et rejette les types incorrects
- 7ExempleCompare les champs sélectionnés
- 8DéfiRepère une rupture du contrat
- 9Point cléVérifie plus d’une paire correspondante
- 10ExplicationLes codes de hachage réduisent le nombre de candidats
- 11ExempleGénère le code de hachage cohérent
- 12DéfiDistingue une collision de l’égalité
- 13ExempleObserve l’effet d’une clé modifiée
- 14LienUn record simple fournit les deux méthodes
- 15DéfiSuis l’ensemble après génération de l’égalité
- 16RécapitulatifExamine une classe de valeur
5Ajoute une fonctionnalité sans mélanger entrées et règles
11 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionRelie les règles aux échanges avec l’utilisateur
- 2ExplicationNomme les deux tâches
- 3ExempleRelie un ensemble d’objets partagés
- 4ExempleRenvoie le résultat pour l’affichage
- 5DéfiPlace le rejet dans la bonne classe
- 6ExplicationDonne une méthode de logique à la nouvelle requête
- 7ExempleVérifie avec des entrées connues
- 8DéfiÉvalue la proposition de stockage
- 9ConseilFais évoluer le programme par étapes vérifiées
- 10DéfiPasse à une autre interface
- 11RécapitulatifExamine les points de passage d’une nouvelle fonctionnalité
Références et égalité en Java : QCM
Série 1 : Suis les valeurs, les appels et les comparaisons simples10 Questions Pratiques
Question 1 sur 10
Que contient original après ce code ?
int original = 8;
int duplicate = original;
duplicate = 11;
Afficher la réponseMasquer la réponse
duplicate : original contient donc toujours 8. Traiter les variables comme des alias donnerait 11, mais une affectation de int copie le nombre lui-même et ne crée aucun objet partagé.Question 2 sur 10
Lamp possède un champ booléen initialement faux ; turnOn() le rend vrai et isOn() le lit. Que renvoient a.isOn() et b.isOn(), dans cet ordre ?
Lamp a = new Lamp();
Lamp b = a;
b.turnOn();
b = new Lamp();
Afficher la réponseMasquer la réponse
a et b. La réaffectation de b le fait ensuite pointer vers une nouvelle lampe dont le champ est initialement faux, tandis que a conserve la lampe allumée. Prévoir deux résultats vrais revient à oublier le nouvel objet créé par la dernière instruction.Question 3 sur 10
label peut valoir null. Quelle condition vérifie sans risque qu’il contient au moins trois caractères ?Afficher la réponseMasquer la réponse
&& saute l’appel qui lit la longueur lorsque ce test est faux. Placer cet appel en premier déréférence déjà l’objet absent. Un seul & évalue aussi les deux côtés : il ne fournit donc pas la protection nécessaire par court-circuit.Question 4 sur 10
Robot r non nul. Elle exécute uniquement r = new Robot();. Qu’arrive-t-il à la variable de l’appelant dont la valeur a été transmise comme argument ?Afficher la réponseMasquer la réponse
Question 5 sur 10
Gauge.copy() renvoie new Gauge(value). Un indicateur commence à 6 et raise() ajoute 1. Quelles valeurs first et second indiquent-ils ?
Gauge first = new Gauge(6);
Gauge second = first.copy();
second.raise();
Afficher la réponseMasquer la réponse
Question 6 sur 10
Device stocke directement son argument mutable Settings. Son accesseur renvoie une copie des paramètres stockés. Deux appareils reçoivent le même objet de paramètres, puis l’appelant modifie cet objet d’origine. Quelle modification de conception rend chaque appareil indépendant de cette modification extérieure et de l’autre appareil ?Afficher la réponseMasquer la réponse
Question 7 sur 10
Sensor s non nul est ajouté à deux listes. Une liste retire son entrée pour s. Qu’arrive-t-il au capteur dans l’autre liste ?Afficher la réponseMasquer la réponse
Question 8 sur 10
Que renvoient ces deux expressions ?
String word = new String("maple");
word == "maple"
word.equals("maple")
Afficher la réponseMasquer la réponse
String.equals compare les caractères et renvoie vrai. Un texte égal ne rend pas identiques des chaînes créées séparément, même si des littéraux égaux peuvent partager un objet.Question 9 sur 10
Object ?Afficher la réponseMasquer la réponse
equals, son type de retour est boolean et son paramètre est de type Object. Reprendre cette déclaration la redéfinit ; ajouter @Override permet au compilateur de vérifier la correspondance. Un paramètre de type Note crée une surcharge et laisse la méthode héritée en place.Question 10 sur 10
Afficher la réponseMasquer la réponse
Série 2 : Applique le partage, les collections et la séparation des responsabilités10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
Qu’arrive-t-il à a après la dernière ligne ?
String a = "river";
String b = a;
b = b.toUpperCase();
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
Que se passe-t-il lors de l’exécution de ce code ?
Integer reading = null;
int value = reading;
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
Meter stocke un entier, avec set(int) et get(). Que renvoie m.get() après l’appel ?
static void adjust(Meter x) {
x.set(14);
x = new Meter(22);
x.set(25);
}
Meter m = new Meter(9);
adjust(m);
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
Bin.items() renvoie directement sa liste privée d’objets Part mutables. Un appelant doit pouvoir examiner et modifier la liste et les pièces renvoyées sans changer le contenu du bac ni l’état des pièces stockées. Que doit contenir le résultat ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
return null dans un else après le test du nom, à l’intérieur de la boucle. Où faut-il placer ce retour ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
copy est créée avec new ArrayList<>(original). Les deux listes contiennent initialement le même Clip c mutable. L’appelant modifie c par copy.get(0), puis vide copy. Que reste-t-il dans original ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
Badge possède des champs et un constructeur, mais ne redéfinit pas equals. Quel est le résultat ?
new Badge("Q7").equals(new Badge("Q7"))
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
Tag.equals compare uniquement code. Tag.hashCode utilise à la fois code et le champ mutable color. Deux étiquettes ont le même code mais des couleurs différentes. Une personne propose de renvoyer un code de hachage constant pour corriger le comportement. Comment faut-il évaluer les deux règles ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
count(), average() et max(). La personne qui le développe ajoute countAbove(limit) pour tout seuil fourni par l’appelant, puis remplace tous les relevés par une simple somme, un effectif et un maximum. Pourquoi la nouvelle requête ne peut-elle plus être implémentée correctement pour tous les journaux ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Série 3 : Débogue les alias, l’égalité et la conception des collections10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
Seules les références indiquées existent et toutes les variables restent utilisables. Cell contient des données de valeur mutables. Combien d’objets cellule restent accessibles ?
Cell a = new Cell(3);
Cell b = new Cell(8);
Cell c = a;
a = b;
b = null;
c = a;
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
private ArrayList<Note> notes;. Son constructeur n’affecte jamais de valeur à notes, et add appelle immédiatement notes.add(note). Quelle modification prépare une instance nouvellement construite à stocker des notes ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
Box possède un champ entier lu par get() et modifié par set(int). Quelles valeurs left.get() et right.get() indiquent-ils après cet appel ?
static void revise(Box x, Box y) {
x.set(10);
x = y;
x.set(16);
}
Box left = new Box(4);
Box right = new Box(7);
revise(left, right);
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
Member stocke une SimpleDate joined non nulle et doit déterminer si son adhésion précède celle d’un autre membre. Où la comparaison des années, des mois et des jours doit-elle être implémentée ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
Queue stocke directement une ArrayList<Job> reçue, mais son accesseur renvoie une nouvelle liste superficielle. L’appelant vide la liste d’entrée d’origine. La file doit conserver son contenu d’origine, tandis que les objets tâche peuvent rester partagés. Quelle correction répond à cette exigence ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
Token déclare public boolean equals(Token other) sans @Override. Les comparaisons directes semblent correctes, mais une ArrayList<Token> ne trouve pas un nouveau jeton dont les champs correspondent. Quel est le problème de signature ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
Key.equals et hashCode utilisent tous deux serial. Une clé mutable est ajoutée à une HashSet, puis son numéro de série change et une recherche échoue. Quelle séquence permet de modifier le numéro de série comme prévu tout en conservant la clé dans l’ensemble ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
countAbove(limit) compte les relevés strictement supérieurs à son argument. Un journal contient 11,5, 15,0, 15,0 et 18,5 °C. Quel résultat une vérification directe de countAbove(15.0) doit-elle attendre ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
Mark.equals compare code. Une liste contient des marques de codes « A », « B », « A » ; les deux marques A sont des objets distincts. Une seconde liste ne contient qu’une référence à la première marque A. Que reste-t-il après marks.removeAll(second) ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Série 4 : Raisonne sur les copies, les contrats et les changements de conception10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
Node contient des données mutables. Seules les références indiquées existent. À la dernière ligne, combien d’objets nœud sont accessibles par first ou par la liste ?
Node first = new Node();
ArrayList<Node> nodes = new ArrayList<>();
nodes.add(first);
first = new Node();
nodes.add(first);
nodes.remove(0);
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
Locker.find(id) renvoie actuellement un nouvel Item mutable dont les valeurs des champs sont identiques à celles de l’article stocké correspondant. Les appelants utilisent le résultat pour mettre à jour le libellé de l’article stocké. Quel comportement la méthode doit-elle offrir pour que ces mises à jour affectent le casier ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
Schedule stockent un même champ SimpleDate mutable et partagé. Une personne rend uniquement les champs des plannings final. Que se passe-t-il si l’objet date d’origine est modifié par son mutateur ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
Document mutables. matching(prefix) construit une nouvelle liste contenant les documents existants qui correspondent. Un appelant retire un résultat et modifie le titre d’un autre. Quel effet cela peut-il avoir sur la bibliothèque ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
x et y, qu’établit x == y lorsque le résultat est vrai ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
Score.equals(Object) accepte un autre score lorsque la différence absolue entre leurs valeurs entières est au plus égale à 2. Les scores x, y et z valent 10, 12 et 14. Aucune valeur ne change. Quel problème relatif au contrat d’égalité apparaît entre ces trois objets ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
record Stamp(String code, int batch) {} utilise une égalité et un hachage générés. Un ensemble reçoit new Stamp("R", 4), un autre new Stamp("R", 4) distinct, puis new Stamp("R", 5). Combien d’entrées contient-il ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
Double.compare(level, other.level) == 0 dans equals. Une méthode de hachage proposée renvoie toujours 1. Une autre renvoie des codes différents pour des objets distincts dont les niveaux valent tous deux NaN. Comment ces propositions se comparent-elles au regard du contrat d’égalité ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Tu bloques sur ton propre exercice ? Aide aux Devoirs
Mots-Clés
A
Accessibilité par référence
Agrégat
Alias
Box second = first;, second est un alias et son mutateur modifie la boîte à laquelle first donne accès.Appartenance à une liste
Argument
gate.allowIn(ana), l’argument est la valeur de référence lue dans ana, que le paramètre copie.ArrayList
ArrayList<Parcel> peut contenir trois références à des colis et permettre à une boucle d’additionner leurs poids en kilogrammes.Autoboxing
numbers.add(12) de transmettre une valeur Integer lorsque numbers est une ArrayList<Integer>.B
Boucle for-each
getWeight() sans modifier le contenu de la liste.C
Cache des objets enveloppes
equals au lieu de te fier à == sur ces objets.Champ de type référence
birthday dans Person peut mener à une SimpleDate qui effectue des comparaisons de dates.Champ final de type référence
add.Champs utilisés pour l’égalité
Classe enveloppe
Integer fournit le type d’élément de ArrayList<Integer> ; le type primitif int ne le peut pas.Clé de HashMap
Code de hachage
Objects.hash(title, year) lorsque ces champs définissent l’égalité.Cohérence
Collision de hachage
equals les distingue correctement.Composition
ConcurrentModificationException
ConcurrentModificationException avant la fin du parcours.Contrat public
Copie défensive
Copie superficielle
D
Délégation
olderThan appelle birthday.before(other.birthday) pour comparer les dates.É
Égalité de contenu
Évaluation en court-circuit
&& lorsque son côté gauche est faux, ce qui permet à un test préalable de protéger un appel ultérieur.name != null && name.length() > 0 saute l’appel de méthode lorsque la référence est nulle.E
Encapsulation
H
HashSet
HashSet<Book> conserve une entrée pour deux livres distincts et égaux lorsque leur égalité et leurs codes de hachage sont cohérents.I
Identité
== pour deux références. Des valeurs de champs correspondantes ne l’établissent pas à elles seules.Immuabilité
toUpperCase() ne peut pas modifier les caractères de la chaîne d’origine.Injection par constructeur
Interface utilisateur
add d’un journal de températures et affiche tout message de rejet.Internalisation des chaînes
"oak" == "oak" vrai et masquer un bogue qui apparaît lorsque le même texte provient d’une saisie.L
Logique applicative
M
Méthode de copie
new Counter(value) afin qu’incrémenter la copie laisse l’original inchangé.Méthode de fabrique
issue() d’une machine à tickets construit un ticket avec son prochain numéro et le renvoie.Mutation
parcel.setWeight(3.5) est une mutation qu’une liste contenant ce colis peut aussi observer.N
NullPointerException
name.length() lève une NullPointerException lorsque la variable name contient null.O
Objet mutable
Objet propriétaire
add.Objet receveur
this référence cet objet.earlier.before(later), l’objet receveur est earlier ; l’autre date est fournie comme argument.P
Paramètre
allowIn(Person visitor), le paramètre visitor reçoit la référence à la personne fournie par l’appelant.Passage par valeur
R
Réaffectation
current = new Counter(); redirige current tandis que les autres alias conservent l’ancien compteur.Record
Book(String title, int year) compare les deux composants et fournit les accesseurs title() et year().Récupération de mémoire
Redéfinition
equals(Object) dans une nouvelle classe.@Override permet au compilateur de vérifier que la méthode d’égalité d’un livre est une redéfinition valide.Référence
Counter second = first; permet aux deux variables d’accéder au compteur d’origine.Référence nulle
null, qui ne mène à aucun objet et peut être contenue dans une variable de type référence, mais pas dans une variable de type primitif.String name ne peut pas recevoir length(), tandis qu’une chaîne vide peut renvoyer une longueur de 0.Réflexivité
x.equals(x).this == other dans equals satisfait la réflexivité avant de comparer les valeurs des champs.Requête sur une collection
songsLongerThan(200) construit une nouvelle liste tout en conservant les références aux morceaux correspondants.Résultat de recherche
S
Surcharge
equals(Book other) crée une surcharge tout en laissant equals(Object) disponible pour les collections.Symétrie
T
Transitivité
Type primitif
int permet à count de contenir directement 12 : l’affecter à une autre variable copie donc 12.Type référence
null.String est un type référence : une variable peut donc référencer un objet texte ou ne contenir aucune référence à un objet.U
Unboxing
NullPointerException pour une référence nulle à un objet enveloppe.int count = boxed; : boxed doit donc référencer un véritable objet Integer.V
Valeur de retour
findByName dans un registre peut mener à une personne stockée que l’appelant et le registre partagent alors.Valeur par défaut d’un champ
ArrayList<Song> non initialisé est null : aucune liste n’existe donc encore.Validation des données
Vérification directe
- 1Premiers programmes Java
- 2Conditions et boucles en Java
- 3Méthodes Java
- 4Listes et tableaux en Java
- 5Classes et objets en Java
- 6Références et égalité en Java
- 7HashMap et collections en Java
- 8Héritage et polymorphisme en Java
- 9Exceptions et fichiers en Java
- 10Paquetages et tests unitaires Java
- 11Généricité et flux en Java
À propos de cette unité
Crée ton propre Matériel d'Étude
Transforme tes notes, diapos et documents en Guides d'Étude, Questions Pratiques, Flashcards et 6 autres outils.