Objets, références et égalité en Java

Découvre ce que contient réellement une variable Java : une valeur primitive ou une référence à un objet. Suis les alias et 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

  1. 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érence
      Nomme 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 inaccessibles
      Suis 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’erreur
      Explique ce que signifie null, reconnais les endroits où cette valeur apparaît, évite NullPointerException et détermine quand null est une valeur pertinente.
  2. 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éthodes
      Pré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.
  3. 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 objets
      Modé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 listes
      Ajoute, 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.
  4. 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 contenu
      Explique 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 equals
      Redé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 à equals
      Explique 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.
  5. 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 applicative
      Dé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ées
      Ajoute 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.

Références et égalité en Java : cours

5 chapitres

1

Suis 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éclarationContenu 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 new cré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);
  1. Dessine la variable a pointant vers l’objet A, de poids 2.
  2. Dessine b pointant vers ce même objet A. Il n’y a toujours qu’une boîte.
  3. Remplace le poids dans l’objet A par 5. Les deux chemins donnent maintenant accès au poids 5.
  4. Crée l’objet B de poids 7 et redirige uniquement b vers 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.

ActionModification dans le diagrammeEffet par les autres alias
b.setWeight(5)Modifier un champ dans l’objetIls voient le nouveau poids
b = new Box(7)Rediriger la flèche de bLeurs flèches restent inchangées
b = nullSupprimer la flèche de bIls 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.

ObjectifExpression sans risque
Vérifier l’absencename == null
Vérifier la présencename != 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

  • null signifie 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.
2

Utilise 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éthodeCe que constate l’appelant
Affecter une valeur à un paramètre primitifAucun 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érenceAucun 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.
  • this dé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.
3

Relie 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 constructeurConséquence pour les références
Stocker la référence à la date reçueL’appelant et la personne peuvent partager la date
Construire une nouvelle date à partir des valeurs reçuesLa personne commence avec sa propre date
Stocker une date immuableLe 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éeEffet sur la liste de lecture
Retirer une entréeLe contenu de la liste de lecture reste inchangé
Vider la copieLe 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.
4

Dé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éesSignification de ==Signification de equals
Valeurs primitivesCompare les valeurs primitivesAucune méthode d’instance sur une valeur primitive
RéférencesMême objet, ou les deux à nullRègle d’égalité de la classe
ChaînesMême objet chaîneContenu en caractères identique
Classe héritant de Object.equalsMême objetMê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 que equals suit la règle de la classe.
  • Object.equals compare l’identité jusqu’à sa redéfinition.
  • Utilise String.equals pour 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) et y.equals(x) donnent le même résultat.
  • Transitive : si x est égal à y et y à z, alors x est é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 pas equals(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 hachageConsé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 1Autorisé, 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 hashCode chaque fois que tu redéfinis equals.
  • 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.
5

Attribue 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.

TravailClasse responsable
Lire le texte d’une commandeInterface utilisateur
Stocker un relevéJournal de températures
Calculer la moyenne ou le maximumJournal de températures
Arrondir et afficher un résultatInterface utilisateur
Rejeter une température impossibleJournal de températures
Expliquer à l’utilisateur qu’une entrée est incorrecteInterface 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.
  • main construit 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

5 Leçons Interactives

1

Suis les variables sans perdre les objets

15 étapes
Suis les copies de valeurs primitives, les références partagées, les réaffectations et null dans un court programme.
  1. 1
    IntroductionDessine 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.

  2. 2
    ExplicationUne valeur peut être une donnée ou une référence

    Java possède huit types primitifs : byte, short, int, long, float, double, char et boolean. 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 ou null.

    Dans int count = 13;, la valeur est 13. Dans Tally t = new Tally(13);, la valeur de t est une référence à un objet qui contient ses propres données.

  3. 3
    ExempleCopie 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. start reste à 13, car aucune instruction ne lui affecte de nouvelle valeur. Dessine deux boîtes contenant des nombres distincts ; aucun objet partagé n’intervient.

  4. 4
    ExempleCopie une valeur de type référence

    Supposons que Tally stocke un entier, que addOne() l’augmente et que value() le lise.

    Tally first = new Tally(13);
    Tally next = first;
    next.addOne();
    

    Un new crée un compteur. L’affectation donne à next un autre chemin vers celui-ci. L’appel à addOne() porte sa valeur stockée à 14, que first.value() lit maintenant aussi. La seconde variable n’est pas un second compteur.

  5. 5
    DéfiDistingue un objet de son alias

    Avec les mêmes méthodes de Tally, que renvoie a.value() ?

    Tally a = new Tally(20);
    Tally b = new Tally(a.value());
    b.addOne();
    

    ---reveal---

    Elle renvoie 20. Le second new cré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.

  6. 6
    LienCertains objets partagés ne peuvent pas être modifiés

    Les chaînes et les classes enveloppes comme Integer sont des types référence. L’autoboxing convertit les valeurs primitives en objets enveloppes pour des usages comme ArrayList<Integer>.

    Les chaînes sont immuables : si left et right partagent « cedar », l’affectation right = right.toUpperCase() ne modifie pas les caractères d’origine. left reste « cedar ». Le résultat renvoyé en majuscules n’est affecté qu’à right.

  7. 7
    AnalogieUtilise 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.

  8. 8
    ExempleModifie d’abord l’état partagé

    Supposons que Crate stocke une masse en kilogrammes, avec les méthodes setMass et mass.

    Crate a = new Crate(3);
    Crate b = a;
    b.setMass(6);
    

    Dessine a et b pointant vers une même caisse. Le mutateur fait passer la masse dans cette caisse de 3 à 6. a.mass() et b.mass() renvoient maintenant tous deux 6 ; le mutateur n’a redirigé aucune des deux variables.

  9. 9
    ExempleRedirige 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 variable a pointe 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.

  10. 10
    Dé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 dans c ; affecter null à a n’a supprimé qu’un seul chemin. La caisse distincte de 9 kg reste accessible par b.

  11. 11
    Point 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.

  12. 12
    ExplicationNull signifie que la destination est absente

    null n’est pas un objet vide. Une String nulle n’a aucun objet sur lequel appeler length(), 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 une NullPointerException lorsque l’unboxing tente d’obtenir une valeur primitive à partir de l’objet enveloppe absent.

  13. 13
    ConseilLis 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.

  14. 14
    Dé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 && saute mass() 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.

  15. 15
    Ré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.

2

Suis un objet à l’entrée et à la sortie d’une méthode

12 étapes
Suis la mutation et la réaffectation d’un paramètre, puis choisis entre des valeurs de retour partagées et indépendantes.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionSuis un appel et son résultat
  2. 2
    ExplicationUn paramètre reçoit sa propre valeur
  3. 3
    ExempleModifie l’objet partagé
  4. 4
    ExempleRéaffecte la référence locale
  5. 5
    DéfiUne méthode peut-elle échanger les variables de l’appelant ?
  6. 6
    LienCompare l’objet receveur à son argument
  7. 7
    ExplicationLe type de retour ne promet pas une copie
  8. 8
    ExempleConstruis un objet indépendant à renvoyer
  9. 9
    DéfiTrouve une entrée qui peut être mise à jour
  10. 10
    ConseilUn accesseur peut révéler un alias caché
  11. 11
    DéfiCombine une valeur de retour avec un appel prenant un paramètre
  12. 12
    RécapitulatifExamine les deux points de passage d’une méthode
3

Construis et protège une collection d’objets

16 étapes
Relie des champs objets et des listes, recherche des entrées stockées et distingue les copies de conteneurs des copies d’éléments.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionDistingue les niveaux d’état
  2. 2
    ExplicationModélise une relation de possession
  3. 3
    ExempleDélègue la règle de comparaison des dates
  4. 4
    DéfiProtège la date reçue
  5. 5
    ExplicationUne entrée est un autre chemin
  6. 6
    ExempleCalcule une somme avec une boucle
  7. 7
    ExempleConserve l’objet sélectionné
  8. 8
    DéfiRenvoie l’absence au bon moment
  9. 9
    ConseilSépare la suppression du parcours
  10. 10
    ExplicationDonne à l’objet propriétaire une liste initialisée
  11. 11
    ExemplePropose des opérations utiles
  12. 12
    DéfiDistingue une copie de liste de ses éléments
  13. 13
    LienProtège les deux directions
  14. 14
    ExempleFiltre dans un nouveau résultat
  15. 15
    DéfiSuis une copie à travers un autre objet
  16. 16
    RécapitulatifExamine les points de passage de la collection
4

Construis une égalité fiable pour les collections

16 étapes
Choisis entre comparaison d’identité et de contenu, implémente ensemble equals et hashCode, et raisonne sur la stabilité des clés.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionDécide ce que signifie une correspondance
  2. 2
    ExempleUn texte égal dans des objets distincts
  3. 3
    ConseilNe laisse pas la mise en cache choisir la règle
  4. 4
    DéfiDonne une égalité de contenu à une classe
  5. 5
    ExplicationChoisis les champs et la signature
  6. 6
    ExempleAccepte l’identité et rejette les types incorrects
  7. 7
    ExempleCompare les champs sélectionnés
  8. 8
    DéfiRepère une rupture du contrat
  9. 9
    Point cléVérifie plus d’une paire correspondante
  10. 10
    ExplicationLes codes de hachage réduisent le nombre de candidats
  11. 11
    ExempleGénère le code de hachage cohérent
  12. 12
    DéfiDistingue une collision de l’égalité
  13. 13
    ExempleObserve l’effet d’une clé modifiée
  14. 14
    LienUn record simple fournit les deux méthodes
  15. 15
    DéfiSuis l’ensemble après génération de l’égalité
  16. 16
    RécapitulatifExamine une classe de valeur
5

Ajoute une fonctionnalité sans mélanger entrées et règles

11 étapes
Relie une interface console à une logique réutilisable, vérifie directement une nouvelle requête et préserve ses besoins de stockage.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionRelie les règles aux échanges avec l’utilisateur
  2. 2
    ExplicationNomme les deux tâches
  3. 3
    ExempleRelie un ensemble d’objets partagés
  4. 4
    ExempleRenvoie le résultat pour l’affichage
  5. 5
    DéfiPlace le rejet dans la bonne classe
  6. 6
    ExplicationDonne une méthode de logique à la nouvelle requête
  7. 7
    ExempleVérifie avec des entrées connues
  8. 8
    DéfiÉvalue la proposition de stockage
  9. 9
    ConseilFais évoluer le programme par étapes vérifiées
  10. 10
    DéfiPasse à une autre interface
  11. 11
    RécapitulatifExamine les points de passage d’une nouvelle fonctionnalité

Références et égalité en Java : QCM

40 Questions Pratiques en 4 séries

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;
  1. 11
  2. 8
  3. 19
  4. 0
Afficher la réponseMasquer la réponse
Réponse : B
La première affectation stocke 8 dans chacune des variables de type primitif. L’affectation suivante ne modifie que 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();
  1. false, false
  2. true, true
  3. false, true
  4. true, false
Afficher la réponseMasquer la réponse
Réponse : D
L’appel allume la lampe partagée par 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 ?
  1. label != null && label.length() >= 3
  2. label.length() >= 3 && label != null
  3. label != null & label.length() >= 3
  4. label.equals(null) && label.length() >= 3
Afficher la réponseMasquer la réponse
Réponse : A
Le test de null est exécuté en premier et && 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

Une méthode reçoit un 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 ?
  1. Elle référence maintenant le nouveau robot.
  2. Elle devient nulle au retour de la méthode.
  3. Elle référence toujours le robot d’origine.
  4. Elle référence une copie du robot d’origine.
Afficher la réponseMasquer la réponse
Réponse : C
Le paramètre reçoit une copie de la référence de l’appelant. Affecter une nouvelle référence à ce paramètre ne modifie que la variable locale de la méthode. Remplacer la référence de l’appelant exigerait une affectation chez l’appelant ; cette méthode ne modifie pas non plus le robot d’origine.

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();
  1. 7 and 7
  2. 6 and 7
  3. 6 and 6
  4. 7 and 6
Afficher la réponseMasquer la réponse
Réponse : B
La méthode de copie crée un second indicateur initialisé à partir de la valeur du premier. Incrémenter ce nouvel objet porte sa valeur à 7 et laisse le premier à 6. Deux valeurs de 7 résulteraient du partage de la référence d’origine, mais la méthode construit explicitement un objet distinct.

Question 6 sur 10

Un constructeur de 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 ?
  1. Stocker directement les paramètres ; renvoyer l’objet stocké par l’accesseur.
  2. Garder chaque constructeur ; ne copier les paramètres que dans l’accesseur.
  3. Copier les paramètres dans chaque constructeur ; garder l’accesseur qui copie.
  4. Créer une copie partagée des paramètres ; la donner aux deux constructeurs.
Afficher la réponseMasquer la réponse
Réponse : C
Les deux appareils partagent actuellement l’objet reçu : l’appelant initial peut donc modifier leur état sans utiliser d’accesseur. Chaque constructeur doit copier cet état reçu pour donner à chaque appareil son propre objet. Un accesseur qui renvoie une copie protège l’accès extérieur, mais ne peut pas supprimer un alias conservé lors de la construction.

Question 7 sur 10

Un 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 ?
  1. Il reste le même objet capteur.
  2. Il est retiré de l’autre liste.
  3. Il est remplacé par un capteur copié.
  4. Il devient un élément nul de la liste.
Afficher la réponseMasquer la réponse
Réponse : A
Retirer une entrée ne modifie que le contenu de cette liste. L’autre liste conserve sa propre référence au capteur : elle garde donc le même objet. La suppression ne détruit pas le capteur et ne recherche pas les références vers lui dans tous les autres conteneurs.

Question 8 sur 10

Que renvoient ces deux expressions ?

String word = new String("maple");
word == "maple"
word.equals("maple")
  1. true, true
  2. false, false
  3. true, false
  4. false, true
Afficher la réponseMasquer la réponse
Réponse : D
La construction explicite crée un objet chaîne différent de celui du littéral : le test d’identité est donc faux. 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

Quelle déclaration redéfinit la méthode d’égalité héritée de Object ?
  1. public boolean equals(Note other)
  2. public int equals(Object other)
  3. public boolean equals(Object other)
  4. public boolean equal(Object other)
Afficher la réponseMasquer la réponse
Réponse : C
La méthode héritée porte le nom 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

Une application console suit des durées de lecture. La même logique prendra plus tard en charge une page web. Où doivent se trouver le calcul du total des secondes et l’affichage de « Total » ?
  1. Le calcul dans la logique ; l’affichage dans l’interface console.
  2. Le calcul et l’affichage dans l’interface console.
  3. Le calcul et l’affichage dans la classe de logique.
  4. L’affichage dans la logique ; le calcul dans l’interface console.
Afficher la réponseMasquer la réponse
Réponse : A
Le total est un calcul sur les données dont les deux interfaces ont besoin : la logique devrait donc le renvoyer. L’interface console choisit son libellé et affiche le résultat. Placer le calcul dans l’interface console obligerait la future interface web à le dupliquer ou à dépendre d’un code propre à la console.
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();
  1. Elle référence maintenant le texte « RIVER ».
  2. Elle devient nulle après l’affectation.
  3. Elle référence toujours le texte « river ».
  4. Elle référence maintenant une chaîne vide.
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;
  1. L’unboxing lève une NullPointerException.
  2. value reçoit la valeur primitive 0.
  3. reading devient un objet enveloppe entier pour 0.
  4. La compilation échoue, car les variables de type enveloppe ne peuvent pas contenir null.
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);
  1. 9
  2. 22
  3. 25
  4. 14
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 ?
  1. Une nouvelle liste contenant les références aux pièces stockées.
  2. Une nouvelle liste contenant des copies indépendantes des pièces.
  3. La liste stockée après la copie en place de chaque pièce.
  4. La liste stockée contenant des indices numériques immuables.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Une recherche par nom parcourt une liste dans l’ordre. Avec les noms « Iris », « Noel », « Sana », elle renvoie null lorsqu’on cherche « Sana ». Elle contient actuellement return null dans un else après le test du nom, à l’intérieur de la boucle. Où faut-il placer ce retour ?
  1. Avant que la boucle ne commence à vérifier les entrées.
  2. Après la fin de la boucle sans correspondance trouvée.
  3. Immédiatement après le premier test de nom réussi.
  4. Dans la boucle, avant la lecture de chaque nouveau nom.
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 ?
  1. Une entrée pour le clip modifié.
  2. Une entrée pour le clip inchangé.
  3. Aucune entrée ; les deux listes sont vides.
  4. Une entrée nulle à la place du clip.
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"))
  1. true, car les valeurs des champs correspondent.
  2. true, car les constructeurs définissent l’égalité.
  3. false, car les chaînes ne peuvent pas être des champs d’égalité.
  4. false, car ce sont des objets distincts.
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 ?
  1. La règle actuelle peut rompre le contrat ; une constante est autorisée mais inefficace.
  2. La règle actuelle est valide ; une constante rompt le hachage des objets distincts.
  3. Les deux règles rompent le contrat, car les codes de hachage doivent être uniques.
  4. Les deux règles sont aussi efficaces, car l’égalité confirme les correspondances.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Un programme de suivi des durées rejette les durées négatives. Où faut-il implémenter le rejet et le message demandant à l’utilisateur de réessayer ?
  1. Le rejet dans l’interface ; le message dans la logique.
  2. Le rejet et le message uniquement dans l’interface.
  3. Le rejet dans la logique ; le message dans l’interface.
  4. Le rejet et le message uniquement dans la logique.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Un journal propose actuellement 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 ?
  1. La moyenne devient incorrecte dès que les relevés contiennent une valeur seuil.
  2. Des listes de relevés peuvent avoir les mêmes agrégats mais des nombres différents au-dessus du seuil.
  3. Un maximum ne peut être calculé qu’en conservant chaque objet relevé d’origine.
  4. Modifier un champ privé exige toujours de modifier aussi chaque commande de l’interface.
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;
  1. Deux objets cellule.
  2. Trois objets cellule.
  3. Aucun objet cellule.
  4. Un objet cellule.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Une classe déclare 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 ?
  1. Déclarer une liste locale dans add et laisser le champ inchangé.
  2. Affecter explicitement null à notes dans le constructeur.
  3. Initialiser notes avec new ArrayList<>().
  4. Rendre notes public afin que l’appelant puisse accéder au champ.
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);
  1. 10 and 16
  2. 16 and 16
  3. 4 and 16
  4. 10 and 7
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Un 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 ?
  1. Dans chaque commande d’interface qui compare des membres.
  2. Dans SimpleDate.before, appelée par la méthode du membre.
  3. Dans Member.toString, relue comme texte par l’appelant.
  4. Dans chaque appelant après avoir rendu publics tous les champs de date.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Un constructeur de 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 ?
  1. Copier aussi la liste reçue dans le constructeur.
  2. Garder le constructeur et copier chaque tâche dans l’accesseur.
  3. Renvoyer la liste interne pour que les deux listes restent synchronisées.
  4. Modifier l’accesseur pour qu’il renvoie chaque fois une nouvelle liste vide.
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 ?
  1. La méthode redéfinit equals, mais les listes ne comparent que les codes de hachage.
  2. La méthode ne peut pas lire les champs privés d’un autre objet jeton.
  3. La méthode surcharge equals et laisse equals(Object) inchangée.
  4. La méthode doit renvoyer un entier pour que les recherches dans les collections fonctionnent.
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 ?
  1. Modifier le numéro, retirer la clé avec le nouveau numéro, puis la rajouter.
  2. Modifier le numéro, appeler contains, puis laisser l’entrée inchangée.
  3. Ajouter la clé une seconde fois, modifier le numéro, puis la retirer une fois.
  4. Retirer la clé avec l’ancien numéro, modifier le numéro, puis la rajouter.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Une méthode d’interface a besoin du nombre de tâches stockées. Que devrait normalement faire la méthode de logique ?
  1. Afficher le nombre sur la console.
  2. Renvoyer le nombre sous forme d’entier.
  3. Lire une commande, puis afficher un libellé.
  4. Ouvrir un scanner pour demander un nombre.
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 ?
  1. 0
  2. 1
  3. 2
  4. 3
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) ?
  1. Uniquement la marque de code « B ».
  2. La marque B et la seconde marque A.
  3. Les deux marques de code « A ».
  4. Aucune marque dans la liste.
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

Une liste doit contenir des entiers qui peuvent être absents. Quel type d’élément permet de stocker un nombre ou null ?
  1. int
  2. Integer
  3. double
  4. boolean
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);
  1. Un objet nœud.
  2. Deux objets nœud.
  3. Trois objets nœud.
  4. Aucun objet nœud.
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 ?
  1. Renvoyer une copie superficielle de la liste du casier.
  2. Renvoyer un nouvel article avec le libellé mis à jour.
  3. Renvoyer la référence à l’article stocké correspondant.
  4. Renvoyer une autre copie après chaque changement de libellé.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Deux objets 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 ?
  1. Seul le planning dont l’accesseur a été appelé voit la modification.
  2. Aucun des deux plannings ne voit la date modifiée.
  3. Chaque planning conserve sa propre copie de l’ancienne date.
  4. Les deux plannings voient toujours la date modifiée.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Une bibliothèque conserve une liste privée d’objets 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 ?
  1. Le contenu change ; tous les titres stockés restent identiques.
  2. Le contenu de la bibliothèque et le titre stocké changent.
  3. Ni le contenu de la bibliothèque ni les titres stockés ne changent.
  4. Le contenu reste identique ; un titre stocké change.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Pour des références d’objets non nulles x et y, qu’établit x == y lorsque le résultat est vrai ?
  1. Leurs résultats toString() définissent l’égalité.
  2. Elles référencent le même objet.
  3. Leurs classes redéfinissent equals ensemble.
  4. Leurs champs n’ont jamais été modifiés.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Une méthode 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 ?
  1. La transitivité échoue : les deux premières paires correspondent, mais pas les extrémités.
  2. La symétrie échoue : inverser une même paire change le résultat de sa comparaison.
  3. La réflexivité échoue : un score ne peut pas être égal à sa propre référence.
  4. La cohérence échoue : répéter une comparaison donne chaque fois un autre résultat.
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 ?
  1. 1
  2. 3
  3. 2
  4. 0
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Une application de tâches a besoin d’une nouvelle commande qui renvoie la plus longue durée stockée. Quel ordre d’implémentation permet une vérification directe avant l’ajout de l’interaction sur la console ?
  1. Lire et afficher les durées dans l’interface, puis copier ce code dans la logique.
  2. Ajouter une requête à la logique, vérifier des durées connues, puis relier la commande d’interface.
  3. Ajouter des messages de console à la requête de logique, puis l’appeler depuis chaque commande.
  4. Rendre publique la liste privée, puis laisser l’interface y effectuer directement la recherche.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Une classe de valeur utilise 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é ?
  1. La constante est interdite ; des codes différents sont exigés pour les objets NaN.
  2. Les deux sont autorisées, car des objets distincts peuvent avoir n’importe quels codes.
  3. La constante est autorisée ; des codes différents pour des valeurs NaN égales sont interdits.
  4. Les deux sont interdites, car chaque valeur double exige un code unique.
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

61 Mots-Clés, chacun avec sa définition

A

Accessibilité par référence
L’accessibilité par référence signifie que le programme en cours d’exécution peut suivre une chaîne de références utilisable jusqu’à un objet, éventuellement par des champs ou des listes.
Une liste préserve l’accessibilité d’un colis même après que la variable locale qui le contenait à l’origine est devenue nulle.
Agrégat
Un agrégat est un indicateur combiné, comme une somme, un effectif ou un maximum, qui décrit des valeurs tout en perdant certaines informations à leur sujet.
Les mêmes agrégats peuvent décrire des listes dont les nombres de valeurs au-dessus d’un seuil diffèrent : cette requête exige donc davantage de données.
Alias
Un alias est une autre variable ou référence qui donne accès au même objet, offrant plusieurs chemins vers un état partagé.
Après Box second = first;, second est un alias et son mutateur modifie la boîte à laquelle first donne accès.
Appartenance à une liste
L’appartenance à une liste décrit les entrées qu’une liste contient ; modifier ce contenu est distinct de la modification des champs d’un objet stocké.
Retirer un morceau modifie le contenu d’une liste, tandis qu’une autre liste de lecture peut encore référencer ce même morceau.
Argument
Un argument est une valeur fournie à l’endroit d’un appel pour initialiser un paramètre correspondant dans la méthode ou le constructeur appelé.
Dans gate.allowIn(ana), l’argument est la valeur de référence lue dans ana, que le paramètre copie.
ArrayList
Une ArrayList est un objet liste redimensionnable dont les entrées sont ordonnées ; les entrées objets sont des références, donc ajouter un objet ne copie pas ses champs.
Une ArrayList<Parcel> peut contenir trois références à des colis et permettre à une boucle d’additionner leurs poids en kilogrammes.
Autoboxing
L’autoboxing est la conversion automatique par Java d’une valeur primitive en référence à l’objet enveloppe correspondant lors d’une affectation ou d’un appel.
L’autoboxing permet à numbers.add(12) de transmettre une valeur Integer lorsque numbers est une ArrayList<Integer>.

B

Boucle for-each
Une boucle for-each visite tour à tour les éléments d’une collection et affecte chaque valeur d’élément à une variable de boucle pour la traiter.
Une boucle for-each sur des colis peut additionner chaque résultat de getWeight() sans modifier le contenu de la liste.

C

Cache des objets enveloppes
Un cache des objets enveloppes réutilise des objets enveloppes pour certaines valeurs, faisant dépendre l’identité des références de la réutilisation plutôt que de l’égalité numérique.
Le cache des objets enveloppes Integer inclut de petites valeurs : utilise donc equals au lieu de te fier à == sur ces objets.
Champ de type référence
Un champ de type référence relie son objet propriétaire à un autre objet ou contient null lorsqu’aucun objet n’est présent.
Le 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
Un champ final de type référence ne peut pas recevoir une autre référence après son initialisation, même si l’objet référencé peut rester mutable.
Un champ final de type référence contenant une liste empêche de remplacer la liste, mais n’empêche pas les appels à add.
Champs utilisés pour l’égalité
Les champs utilisés pour l’égalité sont les données sélectionnées pour définir quand deux instances représentent la même valeur aux fins d’un programme.
Le titre et l’année peuvent être des champs utilisés pour l’égalité de livres : ne modifier que l’emprunteur laisse alors cette égalité inchangée.
Classe enveloppe
Une classe enveloppe représente une valeur primitive sous forme d’objet, ce qui permet d’utiliser des valeurs comme des entiers là où des types référence sont nécessaires.
La classe enveloppe Integer fournit le type d’élément de ArrayList<Integer> ; le type primitif int ne le peut pas.
Clé de HashMap
Une clé de HashMap identifie une valeur associée grâce au hachage et à l’égalité ; son état lié à l’égalité devrait rester stable pendant le stockage.
Une clé de HashMap construite à partir du titre et de l’année peut retrouver un libellé de rayon à l’aide d’un objet livre distinct et égal.
Code de hachage
Un code de hachage est un entier utilisé pour réduire le nombre de candidats dans une collection fondée sur le hachage ; des objets égaux doivent produire des codes égaux.
Un code de hachage cohérent pour un livre peut être calculé avec Objects.hash(title, year) lorsque ces champs définissent l’égalité.
Cohérence
La cohérence exige que des tests d’égalité répétés donnent le même résultat tant que les informations comparées restent inchangées, afin que les résultats restent fiables.
La cohérence permet à un résultat de changer après la modification d’un champ d’égalité, mais pas tant que les données comparées restent fixes.
Collision de hachage
Une collision de hachage se produit lorsque des objets inégaux produisent le même code de hachage ; elle est autorisée, car l’égalité confirme toujours la correspondance.
Une collision de hachage ne fusionne pas des valeurs distinctes dans un ensemble lorsque equals les distingue correctement.
Composition
La composition modélise une relation de possession en donnant à un objet des champs qui référencent d’autres objets, comme la date de naissance d’une personne.
La composition permet à un cours de stocker son enseignant comme objet personne et d’obtenir son nom par une méthode.
ConcurrentModificationException
Une ConcurrentModificationException peut signaler qu’une collection a subi une modification structurelle pendant un parcours fondé sur un itérateur.
Une suppression directe dans une liste pendant une boucle for-each peut provoquer une ConcurrentModificationException avant la fin du parcours.
Contrat public
Un contrat public est le comportement promis sur lequel les appelants comptent lorsqu’ils utilisent les méthodes publiques d’une classe, indépendamment de son stockage privé.
Une modification du stockage ne respecte le contrat public que si chaque requête de température prise en charge fonctionne toujours.
Copie défensive
Une copie défensive est un objet distinct créé pour empêcher le code extérieur de modifier un état protégé par une référence mutable partagée.
Un accesseur de date de naissance renvoie une copie défensive afin qu’un mutateur de date appelé par l’appelant laisse intacte la date de naissance de la personne.
Copie superficielle
Une copie superficielle crée un conteneur externe distinct, mais copie les références de ses éléments, laissant les objets contenus partagés.
Une copie superficielle d’une liste de morceaux peut perdre une entrée indépendamment tout en partageant encore chaque morceau mutable.

D

Délégation
La délégation consiste à demander à un autre objet d’effectuer un travail par ses méthodes, en gardant une règle auprès de l’objet qui possède ses données.
Une personne utilise la délégation lorsque olderThan appelle birthday.before(other.birthday) pour comparer les dates.

É

Égalité de contenu
L’égalité de contenu signifie que les champs choisis par la règle d’égalité d’une classe correspondent, même lorsque les objets ont des identités différentes.
Un catalogue de livres peut définir l’égalité de contenu à partir du titre et de l’année tout en ignorant l’emprunteur actuel.
Évaluation en court-circuit
L’évaluation en court-circuit saute le côté droit de && lorsque son côté gauche est faux, ce qui permet à un test préalable de protéger un appel ultérieur.
L’évaluation en court-circuit fait que name != null && name.length() > 0 saute l’appel de méthode lorsque la référence est nulle.

E

Encapsulation
L’encapsulation protège l’état derrière les méthodes d’une classe afin qu’elle puisse contrôler les modifications et imposer des règles à ses données stockées.
L’encapsulation d’une liste de lecture est affaiblie si un accesseur renvoie la liste privée et que les appelants peuvent la vider directement.

H

HashSet
Une HashSet stocke des éléments distincts en utilisant leurs codes de hachage et leur égalité : ajouter une valeur égale ne crée donc pas une autre entrée.
Une 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é
L’identité signifie qu’il s’agit du même objet ; elle est testée par == pour deux références. Des valeurs de champs correspondantes ne l’établissent pas à elles seules.
Deux livres construits séparément peuvent avoir des titres et des années égaux tout en ayant des identités différentes.
Immuabilité
L’immuabilité signifie que l’état d’un objet ne peut pas changer après sa construction : partager cet objet ne permet donc pas de mutation partagée.
L’immuabilité de String signifie que toUpperCase() ne peut pas modifier les caractères de la chaîne d’origine.
Injection par constructeur
L’injection par constructeur fournit les objets collaborateurs d’un objet comme arguments de constructeur, ce qui lui permet d’utiliser des objets existants partagés.
L’injection par constructeur donne à une interface un scanner et un journal au lieu de créer un journal distinct pour chaque commande.
Interface utilisateur
Une interface utilisateur est le code du programme qui interagit avec l’utilisateur, comme une boucle de commande qui lit du texte et affiche des résultats.
Une interface utilisateur lit une température, appelle la méthode add d’un journal de températures et affiche tout message de rejet.
Internalisation des chaînes
L’internalisation des chaînes partage des objets chaîne pour des littéraux correspondants, ce qui permet à deux expressions littérales identiques d’avoir la même référence.
L’internalisation des chaînes peut rendre "oak" == "oak" vrai et masquer un bogue qui apparaît lorsque le même texte provient d’une saisie.

L

Logique applicative
La logique applicative stocke les données et applique les règles de l’application, en exposant des méthodes qui peuvent fonctionner indépendamment des interactions avec la console.
La logique applicative calcule la température moyenne sous forme d’un nombre qu’une interface console ou web peut afficher.

M

Méthode de copie
Une méthode de copie renvoie un nouvel objet initialisé à partir des données d’un objet existant, en fournissant l’indépendance promise par sa conception.
La méthode de copie d’un compteur utilise new Counter(value) afin qu’incrémenter la copie laisse l’original inchangé.
Méthode de fabrique
Une méthode de fabrique fournit un objet à son appelant, soit en créant une nouvelle instance, soit en réutilisant une instance existante.
La méthode de fabrique issue() d’une machine à tickets construit un ticket avec son prochain numéro et le renvoie.
Mutation
Une mutation modifie l’état d’un objet existant, rendant les valeurs modifiées de ses champs visibles par toutes les références à cet objet.
L’appel parcel.setWeight(3.5) est une mutation qu’une liste contenant ce colis peut aussi observer.

N

NullPointerException
Une NullPointerException est une erreur à l’exécution causée par des opérations qui nécessitent un objet absent, comme un appel d’instance ou un unboxing par une référence nulle.
L’appel name.length() lève une NullPointerException lorsque la variable name contient null.

O

Objet mutable
Un objet mutable permet de modifier son état après sa construction : les alias peuvent donc observer les modifications effectuées par une autre référence.
Une date avec des mutateurs est un objet mutable ; si elle est partagée entre deux personnes, la modifier peut changer les deux dates de naissance stockées.
Objet propriétaire
Un objet propriétaire gère un état par ses propres méthodes, comme une liste de lecture qui contrôle les ajouts à sa liste privée de morceaux.
La liste de lecture est l’objet propriétaire chargé d’imposer un nombre maximal dans sa méthode add.
Objet receveur
L’objet receveur est l’objet sur lequel une méthode d’instance est appelée ; dans cette méthode, this référence cet objet.
Dans earlier.before(later), l’objet receveur est earlier ; l’autre date est fournie comme argument.

P

Paramètre
Un paramètre est une variable déclarée par une méthode ou un constructeur qui reçoit la copie de la valeur d’un argument lors d’un appel.
Dans allowIn(Person visitor), le paramètre visitor reçoit la référence à la personne fournie par l’appelant.
Passage par valeur
Le passage par valeur donne à un paramètre de méthode sa propre copie de la valeur d’un argument, y compris une copie de la référence pour un argument objet.
Le passage par valeur permet à une méthode de rediriger son paramètre personne sans rediriger la variable de l’appelant.

R

Réaffectation
Une réaffectation stocke une nouvelle valeur dans une variable existante ; pour une variable de type référence, elle change l’objet auquel la variable donne accès.
La réaffectation current = new Counter(); redirige current tandis que les autres alias conservent l’ancien compteur.
Record
Un record est une classe de données Java dont les champs des composants sont final et dont les accesseurs, l’égalité, le hachage et la représentation sous forme de chaîne sont générés.
Le record Book(String title, int year) compare les deux composants et fournit les accesseurs title() et year().
Récupération de mémoire
La récupération de mémoire est la récupération automatique par Java de la mémoire des objets inaccessibles, à un moment choisi par son environnement d’exécution.
La mémoire d’un compteur peut être récupérée après que le programme en cours d’exécution a perdu son dernier chemin d’accès vers lui.
Redéfinition
Une redéfinition remplace le comportement d’une méthode d’instance héritée par une déclaration correspondante, comme equals(Object) dans une nouvelle classe.
L’annotation @Override permet au compilateur de vérifier que la méthode d’égalité d’un livre est une redéfinition valide.
Référence
Une référence est une valeur qui mène à un objet ; la copier donne à une autre variable un chemin vers ce même objet.
La référence affectée par Counter second = first; permet aux deux variables d’accéder au compteur d’origine.
Référence nulle
Une référence nulle est la valeur 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.
Une référence nulle dans String name ne peut pas recevoir length(), tandis qu’une chaîne vide peut renvoyer une longueur de 0.
Réflexivité
La réflexivité exige que chaque objet non nul soit considéré comme égal à lui-même : une méthode d’égalité valide renvoie donc vrai pour x.equals(x).
Un test initial this == other dans equals satisfait la réflexivité avant de comparer les valeurs des champs.
Requête sur une collection
Une requête sur une collection sélectionne ou calcule des informations à partir des éléments stockés et renvoie souvent une valeur, un objet ou une nouvelle liste de résultats.
La 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
Un résultat de recherche est la valeur correspondante renvoyée par une requête ; une recherche d’objet peut renvoyer la référence stockée ou null si rien ne correspond.
Le résultat d’une recherche par nom peut être une personne stockée dans un registre, ce qui permet des mises à jour par la référence renvoyée.

S

Surcharge
Une surcharge est une autre méthode de même nom, mais dont les types de paramètres diffèrent ; elle ne remplace pas la déclaration d’origine.
Écrire equals(Book other) crée une surcharge tout en laissant equals(Object) disponible pour les collections.
Symétrie
La symétrie exige qu’inverser deux objets non nuls dans une comparaison d’égalité conserve le résultat vrai ou faux.
La symétrie échoue si un livre accepte la chaîne de son titre comme égale, mais que la chaîne rejette l’objet livre.

T

Transitivité
La transitivité exige que, lorsqu’un objet est égal à un deuxième et que celui-ci est égal à un troisième, le premier soit aussi égal au troisième.
La transitivité fait appartenir trois livres ayant les mêmes champs d’égalité au même groupe de valeurs égales.
Type primitif
Un type primitif stocke directement une donnée ordinaire, comme un nombre ou une valeur booléenne ; Java possède huit types primitifs.
Le type primitif int permet à count de contenir directement 12 : l’affecter à une autre variable copie donc 12.
Type référence
Un type référence est un type de classe, de tableau ou d’interface dont les variables peuvent contenir des références à des objets ou la valeur 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
L’unboxing convertit la valeur stockée dans un objet enveloppe vers son type primitif ; il échoue avec une NullPointerException pour une référence nulle à un objet enveloppe.
L’unboxing se produit dans int count = boxed; : boxed doit donc référencer un véritable objet Integer.

V

Valeur de retour
Une valeur de retour est la valeur qu’une méthode transmet à son appelant ; pour un résultat objet, c’est une référence ou null.
La valeur de retour de 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
Une valeur par défaut d’un champ est la valeur initiale fournie par Java à un champ, comme null pour les références ou false pour les booléens.
La valeur par défaut d’un champ ArrayList<Song> non initialisé est null : aucune liste n’existe donc encore.
Validation des données
La validation des données vérifie si une valeur fournie respecte les règles de l’application avant de l’accepter dans l’état stocké.
La validation des données dans un journal de durées peut rejeter une valeur négative quelle que soit l’interface qui l’appelle.
Vérification directe
Une vérification directe appelle des méthodes de logique avec des entrées connues et compare leurs résultats aux réponses attendues sans passer par une interface utilisateur.
Une vérification directe peut ajouter quatre relevés et vérifier la moyenne sans lire de texte de commande.

À propos de cette unité

Par StudeliSignaler une erreur

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.

Commencer