Sommaire

Sommaire

Espace apprenant · Java 25

Espace apprenant Java

Pour réviser et s'entraîner chapitre par chapitre : le vocabulaire à retenir, tous les exercices et un QCM.

  • Java Débutant : 30 chapitres, 175 exercices
  • Java Avancé : 9 chapitres, 16 exercices
  • Un glossaire par chapitre
  • QCM corrigés instantanément
Démarrer : récupérer les exercicesavec Git · Java 25

Les exercices sont dans un projet IntelliJ hébergé sur Git. Vous le clonez une fois, vous travaillez sur votre propre branche, et vous récupérez les nouveaux exercices en un clic.

Adresse du dépôt
  1. Installer Git et IntelliJ IDEA

    Si ce n'est pas déjà fait : installez Git (git-scm.com/downloads, options par défaut) puis IntelliJ IDEA édition Community (jetbrains.com/idea/download).

  2. Cloner le dépôt

    Sur l'écran d'accueil d'IntelliJ : Clone Repository (ou File > New > Project from Version Control). Collez l'adresse copiée ci-dessus, choisissez un dossier dans vos documents puis cliquez sur Clone. Si IntelliJ demande si le projet est fiable : Trust Project. En terminal : git clone https://github.com/k-schroeder/formation-java.git, puis File > Open sur le dossier formation-java.

  3. Créer votre branche (avant de toucher à quoi que ce soit)

    Votre branche est votre copie personnelle : vous y faites tous les exercices, la branche main reste celle du formateur. Dans IntelliJ, ouvrez le menu Git > New Branch… (dans la nouvelle interface, le menu Git se trouve sous l'icône ☰ en haut à gauche). Nom conseillé : prenom-nom, en minuscules, sans espace ni accent (ex. marie-dupont), puis Create. Le nom de la branche active s'affiche à côté du nom du projet, en haut à gauche (en bas à droite avec l'ancienne interface). Autre méthode, dans l'onglet Terminal en bas : git checkout -b marie-dupont, puis git branch : la branche active est précédée d'une étoile.

  4. Installer la bonne version de Java (JDK 25)

    Si un bandeau indique Project JDK is not defined, cliquez sur Setup SDK. Sinon ouvrez File > Project Structure (Ctrl+Alt+Maj+S) puis Project > SDK > Add SDK > Download JDK. Choisissez Version : 25 et le fournisseur proposé par défaut, puis Download et OK.

  5. Trouver un exercice

    Dans le volet Project à gauche, ouvrez src puis le chapitre, puis l'exercice : l'exercice 7.2.A se trouve dans src > ch07 > ex7_2_a. Ouvrez Main.java (ou le fichier cité dans l'énoncé) et complétez le code.

  6. Lancer le programme

    Cliquez sur le triangle vert à côté de main, puis Run 'Main.main()'. Le résultat s'affiche dans la console, en bas.

  7. Sauvegarder votre travail

    De temps en temps : Git > Commit… (Ctrl+K), cochez vos fichiers, écrivez un court message (« exercices du chapitre 5 ») puis Commit. Un commit est une sauvegarde locale de votre branche.

  8. Récupérer les nouveaux exercices du formateur

    Quand le formateur annonce une mise à jour : Git > Fetch, puis Git > Merge…, choisissez origin/main et Merge into Current. Faites d'abord un commit de votre travail en cours. Vos réponses ne sont pas écrasées : seuls les nouveaux fichiers arrivent. Si IntelliJ signale un conflit sur un fichier, ne paniquez pas : c'est que le formateur et vous avez modifié le même exercice. Gardez votre version (Accept Yours) et prévenez le formateur.

  9. Un dossier .idea dans le projet ?

    C'est la configuration IntelliJ (un petit fichier .iml par exercice, rangé dans .idea/modules). Ne le supprimez pas : IntelliJ en a besoin pour ouvrir le projet.

  10. Un message d'erreur en rouge ?

    C'est normal avant d'avoir complété le code, et volontaire dans les exercices « trouver l'erreur ». Chaque exercice est indépendant : une erreur dans l'un n'empêche pas de lancer les autres.

Le front Angular de la médiathèqueà lancer en début de session · Node.js

Le front est le site que votre application Java alimentera plus tard. Vous ne le modifiez pas : il sert de vitrine pour voir à quoi ressemble la médiathèque que vous construisez (catalogue, emprunt de 5 médias au maximum, espace bibliothécaire). Pour l'instant il utilise ses propres données de test, pas votre code Java.

À faire au début de chaque session : lancez le front avant de commencer le chapitre du jour, puis laissez-le ouvert dans ses deux terminaux. Il suffit ensuite de le garder sous la main dans le navigateur.

Archive ZIP · le projet complet avec son README · Node.js 22.22.3 ou plus récent

  1. Décompresser l'archive

    Rangez le dossier mediatech à côté de vos projets Java, pas dans « Téléchargements ». Le fichier README.md du dossier reprend tout ce qui suit.

  2. Ouvrir un terminal dans le dossier mediatech

    Dans IntelliJ : Open le dossier puis l'onglet Terminal. Ou dans VS Code, ou un terminal classique.

  3. Première fois seulement : installer les dépendances

    npm install (quelques minutes). Vérifiez avant que node -v affiche 22.22.3 ou plus.

  4. Terminal 1 : lancer le serveur de données

    npm run api. Laissez ce terminal ouvert (port 3000).

  5. Terminal 2 : lancer le site

    Ouvrez un deuxième terminal, toujours dans mediatech, puis npm start. Attendez « Application bundle generation complete », puis ouvrez http://localhost:4200.

  6. Essayer

    Parcourez le catalogue, sélectionnez des médias, validez un emprunt. Comptes de test : lecteur@example.com et biblio@example.com (espace bibliothécaire).

Le catalogue est vide ou « Impossible de charger les médias » ? Le serveur de données (terminal 1) n'est pas lancé. Pour remettre les emprunts à zéro : arrêtez-le, npm run api:reset, relancez-le.

Partie 1 · 1 / 14

Les bases

Chapitres 1 à 6

Niveau

Java Débutant

Les bases du langage jusqu'à la programmation orientée objet, pour un développeur back-end PHP.

Chapitre 1 · Les bases

Installation et premier lancement

Objectif. Installer les outils, créer un premier projet et lancer un programme Java qui fonctionne.

Glossaire du chapitre

IDE
Environnement de développement intégré : le logiciel où l'on écrit, lance et corrige son code (ici IntelliJ IDEA).
JDK
Java Development Kit : la boîte à outils du développeur (compilateur, JVM, bibliothèques). Il faut l'installer pour programmer en Java.
JVM
Java Virtual Machine : le programme qui lit et exécute le bytecode. Il permet de faire tourner le même programme sur Windows, Mac ou Linux.
Compilateur (javac)
Outil qui traduit le code que l'on écrit (.java) en bytecode (.class).
Bytecode
Code intermédiaire produit par la compilation, compris par la JVM (fichiers .class).
Système de build
Outil qui compile et assemble le projet (IntelliJ, Maven ou Gradle). Il joue le rôle de chef de chantier.
Projet
Le dossier qui regroupe tous les fichiers d'un programme.

2 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 1.3.A Retrouver les outils

Sans rien écrire, repérez dans IntelliJ :

  1. la zone où l'on tape le code ;
  2. la console de sortie ;
  3. le bouton qui lance le programme.
Voir la solution

L'éditeur occupe la grande zone centrale. La console est la fenêtre du bas (onglet Run). Le bouton de lancement est le triangle vert, en haut à droite et à côté de main.

Ma confianceNon évalué
Non vérifiable

Exercice 1.5.A Lancer le programme de base

Créez un projet Java (build system IntelliJ, JDK 25) et lancez le code généré par IntelliJ avec le bouton vert.

Que voyez-vous dans la console ? Selon la version d'IntelliJ, le code de base affiche un message ou ne produit aucune sortie : dans les deux cas, l'important est que le programme se termine sans erreur.

Voir la solution

Le message Process finished with exit code 0 s'affiche en bas de la console. Le code 0 signifie que tout s'est bien passé. Si une erreur rouge apparaît, vérifiez que le JDK 25 est bien sélectionné dans la configuration du projet.

Ma confianceNon évalué
Non vérifiable

Testez-vous : QCM

QCM · Chapitre 1

  1. Que signifie IDE ?

  2. À quoi sert le JDK ?

  3. Quel build system garde-t-on pour la formation ?

  4. Que se passe-t-il quand on clique sur le bouton vert ?

Chapitre 2 · Les bases

Afficher un message dans la console

Objectif. Écrire sa première instruction, comprendre la structure minimale d'un programme Java et savoir lire une erreur de syntaxe.

Glossaire du chapitre

Instruction
Un ordre donné à l'ordinateur. Elle se termine par un point-virgule ;.
Classe
Le « contenant » d'un programme Java. Son nom est celui du fichier (Main pour Main.java).
main
Le point d'entrée : c'est par cette méthode que Java commence à lire le programme.
Console
La fenêtre en bas de l'IDE où s'affichent les résultats (la sortie standard).
println / print
Affichent du texte ; println passe ensuite à la ligne, print non.
Compilation
Traduction du code source en bytecode, avant l'exécution.
Erreur de syntaxe
Faute d'écriture (; ou accolade oubliés) qui empêche la compilation.
Bloc
Portion de code délimitée par des accolades { }.

4 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 2.1.A Dire bonjour

Écrivez un programme qui affiche exactement :

Console
Bonjour !

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        System.out.println("Bonjour !");
    }
}
Console
Bonjour !
Ma confianceNon évalué
✓ Vérifiable

Exercice 2.3.A Se présenter sur deux lignes

Affichez votre prénom sur une première ligne, puis votre ville sur la deuxième, avec deux println.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        System.out.println("Camille");
        System.out.println("Paris");
    }
}
Console
Camille
Paris
Ma confianceNon évalué
✓ Vérifiable

Exercice 2.3.B Tout sur une seule ligne

Affichez Java est super en utilisant trois instructions print (une par mot), pour que tout reste sur la même ligne.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        System.out.print("Java ");
        System.out.print("est ");
        System.out.print("super");
    }
}
Console
Java est super

Chaque mot garde un espace après lui, sinon ils seraient collés (Javaestsuper).

Ma confianceNon évalué
✓ Vérifiable

Exercice 2.5.A Trouver l'erreur

Ce programme ne compile pas. Trouvez pourquoi et corrigez-le.

Main.java·Java
public class Main {
    public static void main(String[] args) {
        System.out.println("Je suis un programme")
        System.out.println("avec un petit oubli");
    }
}
Voir la solution

Il manque un ; à la fin de la première instruction println.

Main.java·Java
public class Main {
    public static void main(String[] args) {
        System.out.println("Je suis un programme");
        System.out.println("avec un petit oubli");
    }
}
Console
Je suis un programme
avec un petit oubli
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 2

  1. Que fait javac ?

  2. Quel est le rôle de la méthode main ?

  3. Que se passe-t-il si on oublie un point-virgule à la fin d'une instruction ?

  4. Quelle différence entre print et println ?

Chapitre 3 · Les bases

Les types de données

Objectif. Distinguer un texte, un caractère, un nombre entier, un nombre décimal et un booléen, et savoir les écrire correctement en Java.

Glossaire du chapitre

Type
La nature d'une donnée (entier, texte, vrai/faux…). Java a besoin de le connaître pour la traiter.
int
Type des nombres entiers (30).
double
Type des nombres à virgule (1.78, avec un point).
char
Type d'un caractère unique, entre guillemets simples ('K').
boolean
Type qui ne vaut que true ou false.
String
Chaîne de caractères : du texte entre guillemets doubles ("Bonjour"). C'est une classe, d'où la majuscule.
Type primitif
Type de base qui contient directement une valeur (int, double, char, boolean…).

3 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 3.2.A Une lettre

Affichez la lettre J en tant que caractère (char), puis le texte Java en tant que chaîne.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        System.out.println('J');
        System.out.println("Java");
    }
}
Console
J
Java

La lettre seule utilise des guillemets simples, le mot utilise des guillemets doubles.

Ma confianceNon évalué
✓ Vérifiable

Exercice 3.3.A Afficher chaque type

Écrivez un programme qui affiche, sur quatre lignes : le texte Java, le nombre entier 2026, le nombre décimal 3.14 et le booléen false.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        System.out.println("Java");
        System.out.println(2026);
        System.out.println(3.14);
        System.out.println(false);
    }
}
Console
Java
2026
3.14
false
Ma confianceNon évalué
✓ Vérifiable

Exercice 3.3.B Quel type choisir ?

Pour chaque information, indiquez le type le plus adapté (int, double, char, boolean ou String) :

  1. le nombre de frères et sœurs ;
  2. le prix d'un café (1,80 €) ;
  3. l'initiale d'un prénom ;
  4. « l'utilisateur est connecté : oui ou non » ;
  5. le nom d'une ville.
Voir la solution
  1. int : c'est un nombre entier.
  2. double : le prix a une virgule.
  3. char : un seul caractère.
  4. boolean : la réponse est vrai ou faux.
  5. String : c'est du texte.
Ma confianceNon évalué
Non vérifiable

Testez-vous : QCM

QCM · Chapitre 3

  1. Quel type convient pour stocker le nombre 1.78 ?

  2. Que représente 'K' (avec des guillemets simples) ?

  3. Quelles sont les deux seules valeurs possibles d'un boolean ?

  4. Pourquoi String s'écrit-il avec une majuscule ?

Chapitre 4 · Les bases

Les variables

Objectif. Ranger des valeurs dans des « boîtes » nommées, les afficher, les modifier et les réutiliser.

Glossaire du chapitre

Variable
Une « boîte étiquetée » qui mémorise une valeur.
Déclaration
Création d'une variable : type nom = valeur;.
Affectation
Le signe = : il range la valeur de droite dans la variable de gauche (ce n'est pas une égalité mathématique).
Concaténation
Collage de textes et de variables avec l'opérateur +.
\n
Caractère spécial qui provoque un retour à la ligne dans un texte.
camelCase
Convention de nommage : minuscules et majuscule à chaque nouveau mot (prixTotal).

4 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 4.2.A Ma fiche d'identité

Déclarez trois variables : prenom (texte), age (entier) et taille (décimal). Affichez-les dans une seule phrase :

Console
Je m'appelle Camille, j'ai 28 ans et je mesure 1.65 m.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : prenom

        // Déclarez votre variable ici : age

        // Déclarez votre variable ici : taille

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        String prenom = "Camille";
        int age = 28;
        double taille = 1.65;

        System.out.println("Je m'appelle " + prenom + ", j'ai " + age + " ans et je mesure " + taille + " m.");
    }
}
Console
Je m'appelle Camille, j'ai 28 ans et je mesure 1.65 m.
Ma confianceNon évalué
✓ Vérifiable

Exercice 4.4.A Changer de valeur

Déclarez int score = 0, affichez-le, puis donnez-lui la valeur 50 et affichez-le à nouveau. Le programme doit afficher 0 puis 50.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : score

        // Affichez le résultat ici

        // Modifiez la variable score ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int score = 0;
        System.out.println(score);

        score = 50;
        System.out.println(score);
    }
}
Console
0
50
Ma confianceNon évalué
✓ Vérifiable

Exercice 4.4.B Ajouter des points

Déclarez int points = 10. Ajoutez-lui 5 avec points = points + 5;, puis affichez Points : 15.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : points

        // Modifiez la variable points ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int points = 10;
        points = points + 5;

        System.out.println("Points : " + points);
    }
}
Console
Points : 15
Ma confianceNon évalué
✓ Vérifiable

Exercice 4.4.C Échanger deux variables

Deux variables sont déclarées :

  • int a = 3;
  • int b = 8;

Échangez leurs valeurs pour que a vaille 8 et b vaille 3, puis affichez a = 8, b = 3.

Indice : il faut une troisième boîte temporaire.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : a

        // Déclarez votre variable ici : b

        // Déclarez votre variable ici : temporaire

        // Modifiez la variable a ici

        // Modifiez la variable b ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int a = 3;
        int b = 8;

        int temporaire = a;   // on met de côté la valeur de a
        a = b;                // a prend la valeur de b
        b = temporaire;       // b reprend l'ancienne valeur de a

        System.out.println("a = " + a + ", b = " + b);
    }
}
Console
a = 8, b = 3
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 4

  1. Que fait la ligne int age = 30; ?

  2. Après int age = 30; puis age = 20;, que vaut age ?

  3. Quel nom de variable est valide en Java ?

  4. Quel caractère passe à la ligne dans un texte ?

Chapitre 5 · Les bases

Les calculs et les opérateurs

Objectif. Faire des calculs avec Java, éviter le piège de la division entière et connaître les raccourcis ++, -- et le cast.

Glossaire du chapitre

Opérateur
Symbole qui effectue un calcul (+, -, *, /, %).
Modulo (%)
Le reste d'une division entière : 10 % 3 vaut 1.
Division entière
Division de deux int : la partie décimale est supprimée (9 / 5 vaut 1).
Priorité
Ordre des opérations : * et / avant + et -. Les parenthèses forcent l'ordre.
++ / --
Ajoutent ou retirent 1 à une variable (compteur++).
Cast
Conversion explicite d'un type vers un autre : (int) 9.99 donne 9.
ASCII / Unicode
Codes numériques associés aux caractères ('A' vaut 65).

6 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 5.1.A Périmètre d'un rectangle

Un rectangle fait 8 de long et 5 de large (deux variables int). Calculez son périmètre (2 × (longueur + largeur)) et affichez Périmètre : 26.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : longueur

        // Déclarez votre variable ici : largeur

        // Calculez ici : perimetre

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int longueur = 8;
        int largeur = 5;

        int perimetre = 2 * (longueur + largeur);

        System.out.println("Périmètre : " + perimetre);
    }
}
Console
Périmètre : 26
Ma confianceNon évalué
✓ Vérifiable

Exercice 5.2.A Le reste d'une division

Deux variables : int dividende = 17; et int diviseur = 5;. Calculez le reste de la division dans une variable reste et affichez exactement 17 % 5 = 2.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : dividende

        // Déclarez votre variable ici : diviseur

        // Calculez ici : reste

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int dividende = 17;
        int diviseur = 5;

        int reste = dividende % diviseur;

        System.out.println(dividende + " % " + diviseur + " = " + reste);
    }
}
Console
17 % 5 = 2
Ma confianceNon évalué
✓ Vérifiable

Exercice 5.4.A Compter avec ++ et --

Une variable int compteur vaut 10. Incrémentez-la avec ++ et affichez-la, puis décrémentez-la avec -- et affichez-la à nouveau. Résultat attendu : 11 puis 10.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : compteur

        // Modifiez la variable compteur ici

        // Affichez le résultat ici

        // Modifiez la variable compteur ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int compteur = 10;

        compteur++;
        System.out.println(compteur);

        compteur--;
        System.out.println(compteur);
    }
}
Console
11
10
Ma confianceNon évalué
✓ Vérifiable

Exercice 5.5.A La moyenne de deux notes

Deux notes sont données :

  • int note1 = 12;
  • int note2 = 15;

Calculez leur moyenne avec la virgule et affichez Moyenne : 13.5.

Indice : un des deux nombres doit être un double pour éviter la division entière.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : note1

        // Déclarez votre variable ici : note2

        // Calculez ici : moyenne

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int note1 = 12;
        int note2 = 15;

        double moyenne = (note1 + note2) / 2.0;

        System.out.println("Moyenne : " + moyenne);
    }
}
Console
Moyenne : 13.5

En écrivant 2.0 au lieu de 2, la division se fait en décimal. Avec 2, on obtiendrait 13.0.

Ma confianceNon évalué
✓ Vérifiable

Exercice 5.5.B Le code d'une lettre

Une variable char lettre vaut 'A'. Convertissez-la en int avec un cast dans une variable code, puis affichez exactement Le code ASCII de A est 65.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : lettre

        // Déclarez votre variable ici : code

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        char lettre = 'A';

        int code = (int) lettre;

        System.out.println("Le code ASCII de " + lettre + " est " + code);
    }
}
Console
Le code ASCII de A est 65
Ma confianceNon évalué
✓ Vérifiable

Exercice 5.6.A Afficher une facture alignée

Trois articles :

  • Stylo à 1,5 €
  • Cahier à 3,2 €
  • Classeur à 12 €

Avec System.out.printf, affichez chaque article sur une ligne :

  • le nom sur 10 caractères, aligné à gauche ;
  • le prix sur 7 caractères, avec 2 décimales, aligné à droite ;
  • puis le symbole €.

Déroulé attendu :

Console
Stylo        1,50 €
Cahier       3,20 €
Classeur    12,00 €

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution

Le nom utilise %-10s (10 caractères, à gauche) et le prix %7.2f (7 caractères, 2 décimales). %n termine chaque ligne.

Main.java·Java
public class Main {
    public static void main(String[] args) {
        System.out.printf("%-10s%7.2f €%n", "Stylo", 1.5);
        System.out.printf("%-10s%7.2f €%n", "Cahier", 3.2);
        System.out.printf("%-10s%7.2f €%n", "Classeur", 12.0);
    }
}
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 5

  1. Que renvoie 9 / 5 si les deux nombres sont des int ?

  2. Que fait l'opérateur % ?

  3. Que fait (int) lettre si lettre est un char ?

  4. Que fait l'instruction compteur++; ?

Chapitre 6 · Les bases

Lire ce que l'utilisateur tape au clavier

Objectif. Rendre le programme interactif : poser une question à l'utilisateur et utiliser sa réponse. Ce chapitre se termine par un premier mini-projet complet.

Glossaire du chapitre

Scanner
Outil qui lit ce que l'utilisateur tape au clavier.
System.in
L'entrée standard, c'est-à-dire le clavier.
import
Instruction qui rend disponible une classe rangée dans un autre package (import java.util.Scanner;).
Package
Un « tiroir » qui regroupe des classes Java.
nextInt() / nextDouble()
Lisent un nombre entier / décimal.
next() / nextLine()
Lisent un seul mot / une ligne entière.
Buffer
Zone mémoire où le clavier dépose la saisie. Après nextInt(), le retour à la ligne y reste : il faut le vider avec un nextLine().
Type référence
Type d'une variable qui désigne un objet (String, Scanner), à la différence d'un type primitif.

6 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 6.3.A Lire un entier

Lisez un nombre entier avec nextInt() et affichez Vous avez saisi : 7 (si l'utilisateur tape 7).

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : nombre

        // Affichez le résultat ici
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int nombre = scanner.nextInt();

        System.out.println("Vous avez saisi : " + nombre);
    }
}

⌨ Valeur à saisir

Console
7
Vous avez saisi : 7
Ma confianceNon évalué
✓ Vérifiable

Exercice 6.3.B La somme de deux entiers

Lisez deux entiers a puis b, et affichez leur somme : La somme est : 7 (si on tape 3 puis 4).

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : a

        // Lisez la saisie ici : b

        // Calculez ici : somme

        // Affichez le résultat ici
    }
}

⌨ Valeurs à saisir, dans cet ordre

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int a = scanner.nextInt();
        int b = scanner.nextInt();

        int somme = a + b;

        System.out.println("La somme est : " + somme);
    }
}

⌨ Valeurs à saisir, dans cet ordre

Console
3
4
La somme est : 7
Ma confianceNon évalué
✓ Vérifiable

Exercice 6.4.A L'aire d'un rectangle

Lisez la longueur puis la largeur (deux double), calculez l'aire et affichez L'aire est : 15.0 (si on tape 3 puis 5).

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : longueur

        // Lisez la saisie ici : largeur

        // Calculez ici : aire

        // Affichez le résultat ici
    }
}

⌨ Valeurs à saisir, dans cet ordre

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        double longueur = scanner.nextDouble();
        double largeur = scanner.nextDouble();

        double aire = longueur * largeur;

        System.out.println("L'aire est : " + aire);
    }
}

⌨ Valeurs à saisir, dans cet ordre

Console
3
5
L'aire est : 15.0
Ma confianceNon évalué
✓ Vérifiable

Exercice 6.5.A Dire bonjour

Lisez un prénom avec nextLine() et affichez exactement Bonjour, Camille ! (si l'utilisateur tape Camille).

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : prenom

        // Affichez le résultat ici
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String prenom = scanner.nextLine();

        System.out.println("Bonjour, " + prenom + " !");
    }
}

⌨ Valeur à saisir

Console
Camille
Bonjour, Camille !
Ma confianceNon évalué
✓ Vérifiable

Exercice 6.5.B Lire une phrase

Lisez une phrase entière (avec des espaces) dans une variable phrase, puis affichez Vous avez écrit : suivi de la phrase.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : phrase

        // Affichez le résultat ici
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String phrase = scanner.nextLine();

        System.out.println("Vous avez écrit : " + phrase);
    }
}

⌨ Valeur à saisir

Console
Il fait beau aujourd'hui
Vous avez écrit : Il fait beau aujourd'hui

Si on utilisait next() à la place de nextLine(), seul le premier mot serait lu.

Ma confianceNon évalué
✓ Vérifiable

Exercice 6.6.A Synthèse : nom, âge, ville

Lisez trois informations :

  1. le nom complet, avec nextLine() ;
  2. l'âge, avec nextInt() ;
  3. la ville, avec nextLine().

Pensez à vider le buffer entre l'âge et la ville. Affichez chaque information sur sa ligne :

Console
Nom : Camille
Âge : 28
Ville : Paris

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : nom

        // Lisez la saisie ici : age

        // Appelez la méthode nextLine() ici

        // Lisez la saisie ici : ville

        // Affichez le résultat ici
    }
}

⌨ Valeurs à saisir, dans cet ordre

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String nom = scanner.nextLine();

        int age = scanner.nextInt();
        scanner.nextLine();               // vide le buffer

        String ville = scanner.nextLine();

        System.out.println("Nom : " + nom);
        System.out.println("Âge : " + age);
        System.out.println("Ville : " + ville);
    }
}

⌨ Valeurs à saisir, dans cet ordre

Console
Camille
28
Paris
Nom : Camille
Âge : 28
Ville : Paris
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 6

  1. Que faut-il écrire en haut du fichier pour utiliser Scanner ?

  2. Quelle différence entre next() et nextLine() ?

  3. Que se passe-t-il si on appelle scanner.nextLine() juste après scanner.nextInt(), sans précaution ?

  4. Que représente System.in ?

1 gros exercice ou projet

Exercice 6.9.A Le projet complet

Énoncé du projet · Assistant de voyage

Scénario. Vous partez à New York. Là-bas, tout est en miles, en degrés Fahrenheit et en dollars. Le programme prépare le voyage en convertissant les unités.

Énoncé

  1. Demander le prénom du voyageur, puis afficher un message de bienvenue.
  2. Demander la distance jusqu'à l'aéroport en km, la convertir en miles, afficher.
  3. Demander la température prévue à New York en °C, la convertir en °F, afficher.
  4. Demander le budget en euros, le convertir en dollars, afficher.
Conversion Formule
km → miles km * 0.621371
°C → °F celsius * 1.8 + 32
€ → $ euros * taux (taux indicatif de 1.10, à adapter)

Réalisez l'Assistant de voyage en suivant l'énoncé ci-dessus. Pensez à lire le prénom avant les nombres.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Complétez ici

        // Affichez le résultat ici

        // Lisez la saisie ici : prenom

        // Affichez le résultat ici

        // Complétez ici

        // Affichez le résultat ici

        // Lisez la saisie ici : distanceKm

        // Calculez ici : distanceMiles

        // Affichez le résultat ici

        // Complétez ici

        // Affichez le résultat ici

        // Lisez la saisie ici : celsius

        // Calculez ici : fahrenheit

        // Affichez le résultat ici

        // Complétez ici

        // Affichez le résultat ici

        // Lisez la saisie ici : euros

        // Déclarez votre variable ici : taux

        // Calculez ici : dollars

        // Affichez le résultat ici

        // Fermez le Scanner ici
    }
}

⌨ Valeurs à saisir, dans cet ordre

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 1. Prénom
        System.out.print("Quel est votre prénom ? ");
        String prenom = scanner.nextLine();
        System.out.println("Bienvenue " + prenom + ", préparons votre voyage à New York !");

        // 2. Distance jusqu'à l'aéroport
        System.out.print("Distance jusqu'à l'aéroport (en km) : ");
        double distanceKm = scanner.nextDouble();
        double distanceMiles = distanceKm * 0.621371;
        System.out.println(distanceKm + " km représentent " + distanceMiles + " miles");

        // 3. Température à New York
        System.out.print("Température prévue à New York (en °C) : ");
        double celsius = scanner.nextDouble();
        double fahrenheit = celsius * 1.8 + 32;
        System.out.println(celsius + " °C représentent " + fahrenheit + " °F");

        // 4. Budget
        System.out.print("Budget du voyage (en euros) : ");
        double euros = scanner.nextDouble();
        double taux = 1.10; // taux indicatif, à adapter
        double dollars = euros * taux;
        System.out.println(euros + " € représentent " + dollars + " $");

        scanner.close();
    }
}

⌨ Valeurs à saisir, dans cet ordre

Console
Quel est votre prénom ? Léa
Bienvenue Léa, préparons votre voyage à New York !
Distance jusqu'à l'aéroport (en km) : 30
30.0 km représentent 18.64113 miles
Température prévue à New York (en °C) : 5
5.0 °C représentent 41.0 °F
Budget du voyage (en euros) : 1000
1000.0 € représentent 1100.0 $

Pour aller plus loin : convertir le poids de la valise (kg en livres : kg * 2.20462), ou faire les conversions dans l'autre sens pour le retour.

Ma confianceNon évalué
✓ Vérifiable

Partie 2 · 2 / 14

Décider et répéter

Chapitres 7 à 9

Chapitre 7 · Décider et répéter

Les conditions : if / else

Objectif. Faire prendre des décisions au programme : exécuter certaines instructions seulement si une condition est vraie.

Glossaire du chapitre

Condition
Question dont la réponse est vrai ou faux, qui décide du chemin pris par le programme.
if / else
« Si… sinon… » : exécute un bloc si la condition est vraie, un autre sinon.
else if
Teste une nouvelle condition quand les précédentes sont fausses. La première condition vraie gagne.
Opérateur de comparaison
==, !=, <, >, <=, >=.
= et ==
= range une valeur ; == compare deux valeurs.
equals
Méthode qui compare le contenu de deux String.
Méthodes de String
length() (nombre de caractères), charAt(i) (caractère à l'indice i), substring(debut, fin) (un morceau), indexOf(…) (position d'un texte), toUpperCase(), strip()…
Immuable (immutable)
Un String ne change jamais : ses méthodes renvoient un nouveau texte qu'il faut ranger dans une variable.

8 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 7.2.A Pair ou impair

Lisez un entier. Affichez Pair s'il est pair, Impair sinon. Indice : utilisez %.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : nombre

        // Écrivez votre condition ici (if / else)
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int nombre = scanner.nextInt();

        if (nombre % 2 == 0) {
            System.out.println("Pair");
        } else {
            System.out.println("Impair");
        }
    }
}

⌨ Valeur à saisir

Console
7
Impair
Ma confianceNon évalué
✓ Vérifiable

Exercice 7.3.A Majeur ou mineur ?

Lisez un âge. Affichez Majeur s'il est de 18 ou plus, sinon Mineur.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : age

        // Écrivez votre condition ici (if / else)
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int age = scanner.nextInt();

        if (age >= 18) {
            System.out.println("Majeur");
        } else {
            System.out.println("Mineur");
        }
    }
}

⌨ Valeur à saisir

Console
20
Majeur
Ma confianceNon évalué
✓ Vérifiable

Exercice 7.4.A Positif, négatif ou nul

Lisez un entier. Affichez Positif, Négatif ou Nul.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : nombre

        // Écrivez votre condition ici (if / else if / else)
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int nombre = scanner.nextInt();

        if (nombre > 0) {
            System.out.println("Positif");
        } else if (nombre < 0) {
            System.out.println("Négatif");
        } else {
            System.out.println("Nul");
        }
    }
}

⌨ Valeur à saisir

Console
-4
Négatif
Ma confianceNon évalué
✓ Vérifiable

Exercice 7.4.B Le barème de notes

Lisez une note sur 20. Affichez Très bien (16 et plus), Bien (12 à 15), Passable (10 ou 11) ou Insuffisant (moins de 10).

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : note

        // Écrivez votre condition ici (if / else if / else)
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int note = scanner.nextInt();

        if (note >= 16) {
            System.out.println("Très bien");
        } else if (note >= 12) {
            System.out.println("Bien");
        } else if (note >= 10) {
            System.out.println("Passable");
        } else {
            System.out.println("Insuffisant");
        }
    }
}

⌨ Valeur à saisir

Console
13
Bien
Ma confianceNon évalué
✓ Vérifiable

Exercice 7.5.A Le mot de passe

Lisez un mot avec nextLine(). S'il vaut secret, affichez Accès autorisé, sinon Accès refusé.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : motDePasse

        // Écrivez votre condition ici (if / else)
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String motDePasse = scanner.nextLine();

        if (motDePasse.equals("secret")) {
            System.out.println("Accès autorisé");
        } else {
            System.out.println("Accès refusé");
        }
    }
}

⌨ Valeur à saisir

Console
secret
Accès autorisé
Ma confianceNon évalué
✓ Vérifiable

Exercice 7.6.A Initiale et longueur

Lisez un prénom avec nextLine(). Affichez son initiale et sa longueur (si l'utilisateur tape Camille) :

Console
Initiale : C
Longueur : 7

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : prenom

        // Affichez le résultat ici
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String prenom = scanner.nextLine();

        System.out.println("Initiale : " + prenom.charAt(0));
        System.out.println("Longueur : " + prenom.length());
    }
}

⌨ Valeur à saisir

Console
Camille
Initiale : C
Longueur : 7
Ma confianceNon évalué
✓ Vérifiable

Exercice 7.6.B Oui ou non ?

Lisez une réponse avec nextLine() :

  • si l'utilisateur tape oui, quelle que soit la façon de l'écrire (OUI, Oui, avec des espaces autour…), affichez C'est parti ! ;
  • sinon, affichez Tant pis.

Indice : strip() et equalsIgnoreCase.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : reponse

        // Modifiez la variable reponse ici

        // Écrivez votre condition ici (if / else)
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String reponse = scanner.nextLine();
        reponse = reponse.strip();          // retire les espaces autour

        if (reponse.equalsIgnoreCase("oui")) {
            System.out.println("C'est parti !");
        } else {
            System.out.println("Tant pis.");
        }
    }
}

⌨ Valeur à saisir

Console
  Oui  
C'est parti !
Ma confianceNon évalué
✓ Vérifiable

Exercice 7.6.C Le domaine d'une adresse e-mail

Lisez une adresse e-mail :

  • affichez ce qui suit le @ : Domaine : exemple.fr si l'utilisateur tape camille@exemple.fr ;
  • s'il n'y a pas de @, affichez Adresse invalide.

Indice : indexOf renvoie -1 quand il ne trouve rien, et substring(debut) prend la fin du texte.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : adresse

        // Déclarez votre variable ici : arobase

        // Écrivez votre condition ici (if / else)
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String adresse = scanner.nextLine();
        int arobase = adresse.indexOf("@");

        if (arobase == -1) {
            System.out.println("Adresse invalide");
        } else {
            System.out.println("Domaine : " + adresse.substring(arobase + 1));
        }
    }
}

⌨ Valeur à saisir

Console
camille@exemple.fr
Domaine : exemple.fr
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 7

  1. Quelle écriture compare deux valeurs ?

  2. Comment comparer correctement deux String ?

  3. Avec if (n >= 10) {…} else if (n >= 16) {…}, que se passe-t-il pour n = 18 ?

  4. Que vaut 7 != 7 ?

  5. Que vaut "Bonjour".substring(0, 3) ?

  6. Après String s = "java"; s.toUpperCase();, que vaut s ?

Chapitre 8 · Décider et répéter

Opérateurs logiques, ternaire et switch

Objectif. Combiner plusieurs conditions (&&, ||, !), écrire une condition en une ligne (ternaire) et choisir parmi plusieurs valeurs (switch).

Glossaire du chapitre

ET (&&)
Vrai quand les deux conditions sont vraies.
OU (||)
Vrai quand au moins une condition est vraie.
NON (!)
Inverse une condition.
Opérateur ternaire
condition ? siVrai : siFaux : un if/else écrit en une ligne.
switch
Compare une variable à plusieurs valeurs précises (case).
break
Sort du switch. Sans lui, Java continue dans le case suivant.
default
Le cas « tout le reste » d'un switch.

4 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 8.1.A Adolescent ?

Lisez un âge. Affichez Adolescent s'il est entre 12 et 17 inclus, sinon Pas adolescent.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : age

        // Écrivez votre condition ici (if / else)
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int age = scanner.nextInt();

        if (age >= 12 && age <= 17) {
            System.out.println("Adolescent");
        } else {
            System.out.println("Pas adolescent");
        }
    }
}

⌨ Valeur à saisir

Console
15
Adolescent
Ma confianceNon évalué
✓ Vérifiable

Exercice 8.2.A Le plus grand de trois

Écrivez un programme qui demande trois nombres entiers à l'utilisateur et affiche le plus grand des trois.

  1. Avec un Scanner, lisez trois entiers avec nextInt() et rangez-les dans trois variables (a, b et c).
  2. Comparez-les avec des if / else if / else, en combinant deux comparaisons avec &&. Par exemple, a est le plus grand si a est supérieur ou égal à b et à c.
  3. Affichez le résultat sous la forme Le plus grand : 8.

Exemple : si l'utilisateur saisit 8, 3 puis 5, le programme affiche Le plus grand : 8. Si deux nombres sont égaux et plus grands que le troisième, n'importe lequel des deux convient.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : a

        // Lisez la saisie ici : b

        // Lisez la saisie ici : c

        // Écrivez votre condition ici (if / else if / else)
    }
}

⌨ Valeurs à saisir, dans cet ordre

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int a = scanner.nextInt();
        int b = scanner.nextInt();
        int c = scanner.nextInt();

        if (a >= b && a >= c) {
            System.out.println("Le plus grand : " + a);
        } else if (b >= a && b >= c) {
            System.out.println("Le plus grand : " + b);
        } else {
            System.out.println("Le plus grand : " + c);
        }
    }
}

⌨ Valeurs à saisir, dans cet ordre

Console
8
3
5
Le plus grand : 8
Ma confianceNon évalué
✓ Vérifiable

Exercice 8.3.A Ternaire

Lisez un entier. Avec un ternaire, stockez "pair" ou "impair" dans une variable parite et affichez-la.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : nombre

        // Calculez ici : parite

        // Affichez le résultat ici
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int nombre = scanner.nextInt();

        String parite = (nombre % 2 == 0) ? "pair" : "impair";

        System.out.println(nombre + " est " + parite);
    }
}

⌨ Valeur à saisir

Console
9
9 est impair
Ma confianceNon évalué
✓ Vérifiable

Exercice 8.4.A Le jour de la semaine

Lisez un numéro de 1 à 7 et affichez le nom du jour avec un switch (1 = Lundi … 7 = Dimanche). Pour tout autre numéro, affichez Numéro invalide.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Lisez la saisie ici : jour

        // Écrivez votre switch ici
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int jour = scanner.nextInt();

        switch (jour) {
            case 1:
                System.out.println("Lundi");
                break;
            case 2:
                System.out.println("Mardi");
                break;
            case 3:
                System.out.println("Mercredi");
                break;
            case 4:
                System.out.println("Jeudi");
                break;
            case 5:
                System.out.println("Vendredi");
                break;
            case 6:
                System.out.println("Samedi");
                break;
            case 7:
                System.out.println("Dimanche");
                break;
            default:
                System.out.println("Numéro invalide");
        }
    }
}

⌨ Valeur à saisir

Console
3
Mercredi
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 8

  1. Que vaut true && false ?

  2. Quand a || b est-il vrai ?

  3. Que se passe-t-il si on oublie break dans un case ?

  4. Que vaut (5 > 3) ? "oui" : "non" ?

1 gros exercice ou projet

Exercice 8.5.A Le projet complet

Énoncé du projet · Défi running

Scénario. Un coureur prépare un 10 km. Le programme lui donne son allure et son niveau.

  1. Demander le prénom, puis la durée de sa course en minutes (nombre entier) pour 10 km.
  2. Calculer l'allure : minutes par kilomètre (duree / 10.0).
  3. Afficher un niveau : moins de 40 min → Expert, moins de 55 min → Confirmé, moins de 70 min → Régulier, sinon Débutant.
  4. Afficher un message spécial si la durée est exactement de 60 minutes : Pile une heure !.

Réalisez le Défi running.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Affichez le résultat ici

        // Lisez la saisie ici : prenom

        // Affichez le résultat ici

        // Lisez la saisie ici : duree

        // Calculez ici : allure

        // Affichez le résultat ici

        // Déclarez votre variable ici : niveau

        // Écrivez votre condition ici (if / else if / else)

        // Affichez le résultat ici

        // Écrivez votre condition ici (if)

        // Fermez le Scanner ici
    }
}

⌨ Valeurs à saisir, dans cet ordre

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Prénom : ");
        String prenom = scanner.nextLine();

        System.out.print("Durée du 10 km (en minutes) : ");
        int duree = scanner.nextInt();

        double allure = duree / 10.0;
        System.out.println(prenom + ", votre allure est de " + allure + " min/km");

        String niveau;
        if (duree < 40) {
            niveau = "Expert";
        } else if (duree < 55) {
            niveau = "Confirmé";
        } else if (duree < 70) {
            niveau = "Régulier";
        } else {
            niveau = "Débutant";
        }
        System.out.println("Niveau : " + niveau);

        if (duree == 60) {
            System.out.println("Pile une heure !");
        }

        scanner.close();
    }
}

⌨ Valeurs à saisir, dans cet ordre

Console
Prénom : Camille
Durée du 10 km (en minutes) : 52
Camille, votre allure est de 5.2 min/km
Niveau : Confirmé
Ma confianceNon évalué
✓ Vérifiable

Chapitre 9 · Décider et répéter

Les boucles

Objectif. Répéter des instructions sans les recopier : while, for, do…while, et savoir sortir d'une boucle.

Glossaire du chapitre

Boucle
Structure qui répète des instructions tant qu'une condition est vraie.
while
Boucle « tant que » : la condition est testée avant chaque tour.
for
Boucle utilisée quand on connaît le nombre de tours : initialisation ; condition ; modification.
do … while
Boucle qui s'exécute au moins une fois, puis teste sa condition.
Itération
Un tour de boucle.
Boucle infinie
Boucle dont la condition ne devient jamais fausse : le programme ne s'arrête plus.
break / continue
break quitte la boucle ; continue passe directement au tour suivant.
Accumulateur
Variable qui cumule un résultat à chaque tour (somme, compteur).

5 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 9.2.A Le compte à rebours

Affichez 5, 4, 3, 2, 1 puis Décollage !. Indice : i--.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Écrivez votre boucle ici (for)

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        for (int i = 5; i >= 1; i--) {
            System.out.println(i);
        }
        System.out.println("Décollage !");
    }
}
Console
5
4
3
2
1
Décollage !
Ma confianceNon évalué
✓ Vérifiable

Exercice 9.3.A Compter jusqu'à 10

Affichez les nombres de 1 à 10, un par ligne, avec une boucle for.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Écrivez votre boucle ici (for)
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            System.out.println(i);
        }
    }
}
Console
1
2
3
4
5
6
7
8
9
10
Ma confianceNon évalué
✓ Vérifiable

Exercice 9.3.B Les nombres pairs

Affichez les nombres pairs de 2 à 20. Indice : i % 2 == 0, ou bien i += 2.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Écrivez votre boucle ici (for)
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        for (int i = 2; i <= 20; i += 2) {
            System.out.println(i);
        }
    }
}
Console
2
4
6
8
10
12
14
16
18
20
Ma confianceNon évalué
✓ Vérifiable

Exercice 9.4.A Redemander jusqu'à un bon nombre

Demandez un nombre entre 1 et 10 avec un do…while, et recommencez tant que la saisie est hors de cet intervalle. Terminez en affichant Merci !.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Déclarez votre variable ici : nombre

        // Écrivez votre boucle ici (do…while)

        // Affichez le résultat ici
    }
}

⌨ Valeurs à saisir, dans cet ordre

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int nombre;

        do {
            System.out.print("Un nombre entre 1 et 10 : ");
            nombre = scanner.nextInt();
        } while (nombre < 1 || nombre > 10);

        System.out.println("Merci !");
    }
}

⌨ Valeurs à saisir, dans cet ordre

Console
Un nombre entre 1 et 10 : 15
Un nombre entre 1 et 10 : 7
Merci !
Ma confianceNon évalué
✓ Vérifiable

Exercice 9.6.A La somme de 1 à 100

Calculez la somme des entiers de 1 à 100 et affichez Somme : 5050.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : somme

        // Écrivez votre boucle ici (for)

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int somme = 0;

        for (int i = 1; i <= 100; i++) {
            somme += i;
        }

        System.out.println("Somme : " + somme);
    }
}
Console
Somme : 5050

Pourquoi 5050 ? somme part de 0, et chaque tour de boucle lui ajoute i : 0 + 1 = 1, puis 1 + 2 = 3, puis 3 + 3 = 6, puis 6 + 4 = 10… jusqu'à i = 100. À la fin, somme contient 1 + 2 + 3 + … + 100 = 5050. On peut le vérifier sans boucle : 100 × 101 ÷ 2 = 5050 (c'est la formule attribuée à Gauss).

Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 9

  1. Quelle boucle s'exécute au moins une fois, même si la condition est fausse dès le départ ?

  2. Que fait break dans une boucle ?

  3. Que provoque l'oubli de compteur++ dans un while (compteur <= 5) ?

  4. Combien de fois s'exécute for (int i = 0; i < 4; i++) ?

2 gros exercices ou projets

Exercice 9.7.A Le projet complet

Énoncé du projet · le coffre-fort

Scénario. Un coffre-fort est protégé par un code secret. L'utilisateur a 3 essais.

  1. Le code secret est 4821 (variable int).
  2. Tant qu'il reste des essais, demander le code.
  3. Si le code est bon : afficher Coffre ouvert ! et sortir de la boucle.
  4. Sinon, afficher le nombre d'essais restants.
  5. Après 3 échecs, afficher Coffre verrouillé.

Réalisez le coffre-fort.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Déclarez votre variable ici : codeSecret

        // Déclarez votre variable ici : essaisMax

        // Déclarez votre variable ici : ouvert

        // Écrivez votre boucle ici (for)

        // Écrivez votre condition ici (if / else)

        // Fermez le Scanner ici
    }
}

⌨ Valeurs à saisir, dans cet ordre

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int codeSecret = 4821;
        int essaisMax = 3;
        boolean ouvert = false;

        for (int essai = 1; essai <= essaisMax; essai++) {
            System.out.print("Code (essai " + essai + "/" + essaisMax + ") : ");
            int saisie = scanner.nextInt();

            if (saisie == codeSecret) {
                ouvert = true;
                break;
            }
            System.out.println("Mauvais code. Essais restants : " + (essaisMax - essai));
        }

        if (ouvert) {
            System.out.println("Coffre ouvert !");
        } else {
            System.out.println("Coffre verrouillé.");
        }

        scanner.close();
    }
}

⌨ Valeurs à saisir, dans cet ordre

Console
Code (essai 1/3) : 1111
Mauvais code. Essais restants : 2
Code (essai 2/3) : 4821
Coffre ouvert !
Ma confianceNon évalué
✓ Vérifiable

Exercice 9.7.B Gros exercice : la caisse de la boulangerie (menu en boucle)

Vous allez écrire un vrai petit programme interactif : la caisse d'une boulangerie. Il affiche un menu, lit le choix de l'utilisateur, agit selon ce choix, puis recommence jusqu'à ce qu'on choisisse de quitter. C'est l'exercice de synthèse du chapitre : il combine la boucle while (ou do…while) et le switch.

Le menu à afficher à chaque tour :

Console
===== Caisse boulangerie =====
1. Ajouter une baguette (1,10 €)
2. Ajouter un croissant (1,20 €)
3. Ajouter un pain au chocolat (1,30 €)
4. Voir le total
5. Encaisser et quitter
Votre choix :

Règles :

  • Gardez un total (double) et un nombre d'articles (int), tous deux initialisés à 0 avant la boucle.
  • À chaque tour : afficher le menu, lire le choix avec nextInt(), puis un switch sur ce choix.
  • cas 1, 2, 3 : ajouter le prix au total, augmenter le nombre d'articles, et confirmer (Ajouté.).
  • cas 4 : afficher le nombre d'articles et le total.
  • cas 5 : afficher le ticket final et sortir de la boucle.
  • default : afficher Choix invalide..
  • La boucle tourne tant que l'utilisateur n'a pas choisi 5.

Exemple de déroulé (l'utilisateur tape 1, puis 2, puis 4, puis 5) :

⌨ Valeurs à saisir, dans cet ordre

Console
===== Caisse boulangerie =====
1. Ajouter une baguette (1,10 €)
2. Ajouter un croissant (1,20 €)
3. Ajouter un pain au chocolat (1,30 €)
4. Voir le total
5. Encaisser et quitter
Votre choix : 1
Ajouté.
===== Caisse boulangerie =====
1. Ajouter une baguette (1,10 €)
2. Ajouter un croissant (1,20 €)
3. Ajouter un pain au chocolat (1,30 €)
4. Voir le total
5. Encaisser et quitter
Votre choix : 2
Ajouté.
===== Caisse boulangerie =====
1. Ajouter une baguette (1,10 €)
2. Ajouter un croissant (1,20 €)
3. Ajouter un pain au chocolat (1,30 €)
4. Voir le total
5. Encaisser et quitter
Votre choix : 4
2 article(s), total : 2.3 €
===== Caisse boulangerie =====
1. Ajouter une baguette (1,10 €)
2. Ajouter un croissant (1,20 €)
3. Ajouter un pain au chocolat (1,30 €)
4. Voir le total
5. Encaisser et quitter
Votre choix : 5
----- Ticket -----
2 article(s)
Total à payer : 2.3 €
Merci, au revoir !

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Déclarez votre variable ici : total

        // Déclarez votre variable ici : articles

        // Déclarez votre variable ici : actif

        // Écrivez votre boucle ici (while)

        // Appelez la méthode close() ici
    }
}
Voir la solution

Une solution avec un drapeau boolean actif et un switch classique (case … : et break). Sans break, Java continuerait dans le case suivant. On aurait aussi pu utiliser do…while : les deux sont corrects.

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        double total = 0;
        int articles = 0;
        boolean actif = true;

        while (actif) {
            System.out.println("===== Caisse boulangerie =====");
            System.out.println("1. Ajouter une baguette (1,10 €)");
            System.out.println("2. Ajouter un croissant (1,20 €)");
            System.out.println("3. Ajouter un pain au chocolat (1,30 €)");
            System.out.println("4. Voir le total");
            System.out.println("5. Encaisser et quitter");
            System.out.print("Votre choix : ");
            int choix = sc.nextInt();

            switch (choix) {
                case 1:
                    total += 1.10;
                    articles++;
                    System.out.println("Ajouté.");
                    break;
                case 2:
                    total += 1.20;
                    articles++;
                    System.out.println("Ajouté.");
                    break;
                case 3:
                    total += 1.30;
                    articles++;
                    System.out.println("Ajouté.");
                    break;
                case 4:
                    System.out.println(articles + " article(s), total : " + total + " €");
                    break;
                case 5:
                    System.out.println("----- Ticket -----");
                    System.out.println(articles + " article(s)");
                    System.out.println("Total à payer : " + total + " €");
                    System.out.println("Merci, au revoir !");
                    actif = false;
                    break;
                default:
                    System.out.println("Choix invalide.");
            }
        }
        sc.close();
    }
}
Ma confianceNon évalué
✓ Vérifiable

Partie 3 · 3 / 14

Stocker plusieurs données

Chapitres 10 à 11

Chapitre 10 · Stocker plusieurs données

Les tableaux

Objectif. Ranger plusieurs valeurs du même type dans une seule variable, les parcourir avec une boucle et faire des calculs dessus.

Glossaire du chapitre

Tableau
Une rangée de cases numérotées contenant des valeurs du même type, de taille fixe.
Indice
Numéro d'une case. Le premier est 0.
length
Nombre de cases d'un tableau (notes.length).
for-each
Boucle qui prend chaque élément d'un tableau un par un : for (int n : notes).
ArrayIndexOutOfBoundsException
Erreur levée quand on demande une case qui n'existe pas.
Valeur par défaut
Valeur d'une case non remplie : 0, false ou null.

5 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 10.3.A Premier et dernier

Pour le tableau de prénoms {"Léa", "Hugo", "Inès", "Noé"}, affichez le premier et le dernier prénom. Le dernier doit se calculer avec length.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : prenoms

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        String[] prenoms = {"Léa", "Hugo", "Inès", "Noé"};

        System.out.println("Premier : " + prenoms[0]);
        System.out.println("Dernier : " + prenoms[prenoms.length - 1]);
    }
}
Console
Premier : Léa
Dernier : Noé
Ma confianceNon évalué
✓ Vérifiable

Exercice 10.4.A Afficher un tableau

Créez le tableau {5, 10, 15, 20} et affichez chaque valeur sur une ligne avec un for-each.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : valeurs

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int[] valeurs = {5, 10, 15, 20};

        for (int valeur : valeurs) {
            System.out.println(valeur);
        }
    }
}
Console
5
10
15
20
Ma confianceNon évalué
✓ Vérifiable

Exercice 10.4.B Saisir des notes

Demandez à l'utilisateur 4 notes, rangez-les dans un tableau, puis affichez leur moyenne.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Créez votre tableau ici : notes

        // Déclarez votre variable ici : somme

        // Écrivez votre boucle ici (for)

        // Calculez ici : moyenne

        // Affichez le résultat ici
    }
}

⌨ Valeurs à saisir, dans cet ordre

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int[] notes = new int[4];
        int somme = 0;

        for (int i = 0; i < notes.length; i++) {
            System.out.print("Note " + (i + 1) + " : ");
            notes[i] = scanner.nextInt();
            somme += notes[i];
        }

        double moyenne = (double) somme / notes.length;
        System.out.println("Moyenne : " + moyenne);
    }
}

⌨ Valeurs à saisir, dans cet ordre

Console
Note 1 : 12
Note 2 : 15
Note 3 : 9
Note 4 : 18
Moyenne : 13.5
Ma confianceNon évalué
✓ Vérifiable

Exercice 10.5.A Somme d'un tableau

Calculez et affichez la somme du tableau {4, 8, 15, 16, 23, 42}. Résultat : 108.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : valeurs

        // Déclarez votre variable ici : somme

        // Parcourez avec une boucle for-each ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int[] valeurs = {4, 8, 15, 16, 23, 42};
        int somme = 0;

        for (int valeur : valeurs) {
            somme += valeur;
        }

        System.out.println(somme);
    }
}
Console
108
Ma confianceNon évalué
✓ Vérifiable

Exercice 10.5.B Le plus petit

Trouvez et affichez la plus petite valeur du tableau {14, 3, 27, 9, 11}.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : valeurs

        // Déclarez votre variable ici : minimum

        // Parcourez avec une boucle for-each ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int[] valeurs = {14, 3, 27, 9, 11};
        int minimum = valeurs[0];

        for (int valeur : valeurs) {
            if (valeur < minimum) {
                minimum = valeur;
            }
        }

        System.out.println("Minimum : " + minimum);
    }
}
Console
Minimum : 3
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 10

  1. Quel est l'indice du premier élément d'un tableau ?

  2. Que renvoie tableau.length pour int[] tableau = {3, 6, 9}; ?

  3. Que se passe-t-il avec notes[5] dans un tableau de 5 cases ?

  4. Peut-on ajouter une 6ᵉ case à un tableau de 5 cases ?

1 gros exercice ou projet

Exercice 10.6.A Le projet complet

Énoncé du projet · la station météo

Scénario. Une station relève la température à midi pendant une semaine.

  1. Créer le tableau double[] temperatures = {14.5, 16.0, 12.5, 18.0, 20.5, 19.0, 15.5};.
  2. Afficher chaque relevé avec le numéro du jour.
  3. Calculer et afficher la moyenne, la température maximale et le nombre de jours au-dessus de 16 °C.

Réalisez la station météo.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : temperatures

        // Déclarez votre variable ici : somme

        // Déclarez votre variable ici : maximum

        // Déclarez votre variable ici : joursChauds

        // Écrivez votre boucle ici (for)

        // Calculez ici : moyenne

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        double[] temperatures = {14.5, 16.0, 12.5, 18.0, 20.5, 19.0, 15.5};

        double somme = 0;
        double maximum = temperatures[0];
        int joursChauds = 0;

        for (int i = 0; i < temperatures.length; i++) {
            System.out.println("Jour " + (i + 1) + " : " + temperatures[i] + " °C");

            somme += temperatures[i];
            if (temperatures[i] > maximum) {
                maximum = temperatures[i];
            }
            if (temperatures[i] > 16) {
                joursChauds++;
            }
        }

        double moyenne = somme / temperatures.length;

        System.out.println("Moyenne : " + moyenne + " °C");
        System.out.println("Maximum : " + maximum + " °C");
        System.out.println("Jours au-dessus de 16 °C : " + joursChauds);
    }
}
Console
Jour 1 : 14.5 °C
Jour 2 : 16.0 °C
Jour 3 : 12.5 °C
Jour 4 : 18.0 °C
Jour 5 : 20.5 °C
Jour 6 : 19.0 °C
Jour 7 : 15.5 °C
Moyenne : 16.571428571428573 °C
Maximum : 20.5 °C
Jours au-dessus de 16 °C : 3
Ma confianceNon évalué
✓ Vérifiable

Chapitre 11 · Stocker plusieurs données

Les collections

Objectif. Utiliser des « listes » qui grandissent toutes seules. On apprend trois collections (ArrayList, HashSet, HashMap), les plus utilisées, et on sait choisir la bonne.

Glossaire du chapitre

Collection
Conteneur « intelligent » qui s'agrandit et se rétrécit seul.
ArrayList
Liste ordonnée, avec doublons possibles, accessible par numéro. Plus tard (chapitre sur les interfaces), vous verrez l'écriture courante List<String> noms = new ArrayList<>(); : même liste, mais la variable annonce seulement « une liste ».
HashSet
Ensemble sans doublon et sans ordre garanti.
HashMap
Dictionnaire qui associe une clé à une valeur.
Clé / valeur
Dans une HashMap, la clé permet de retrouver la valeur (nom → numéro).
Générique < >
Précise le type du contenu : ArrayList<String>.
Classe enveloppe
Version « objet » d'un type primitif (Integer pour int), nécessaire dans les collections.
Autoboxing
Conversion automatique int ↔ Integer.

6 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 11.4.A Ma liste de courses

Créez une ArrayList<String>, ajoutez 3 articles, puis affichez-les avec un for-each, un par ligne, précédés de -.

Votre code à compléter

Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : courses

        // Appelez la méthode add() ici

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<String> courses = new ArrayList<>();
        courses.add("Pain");
        courses.add("Lait");
        courses.add("Pommes");

        for (String article : courses) {
            System.out.println("- " + article);
        }
    }
}
Console
- Pain
- Lait
- Pommes
Ma confianceNon évalué
✓ Vérifiable

Exercice 11.4.B Compter et retirer

Avec la liste Pain, Lait, Pommes : affichez sa taille, retirez Lait, puis affichez la nouvelle taille.

Votre code à compléter

Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : courses

        // Appelez la méthode add() ici

        // Affichez le résultat ici

        // Appelez la méthode remove() ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<String> courses = new ArrayList<>();
        courses.add("Pain");
        courses.add("Lait");
        courses.add("Pommes");

        System.out.println(courses.size());   // 3
        courses.remove("Lait");
        System.out.println(courses.size());   // 2
    }
}
Console
3
2
Ma confianceNon évalué
✓ Vérifiable

Exercice 11.4.C La moyenne d'une liste de notes

Créez une ArrayList<Integer> avec les notes 12, 15, 9, 18. Calculez et affichez leur moyenne.

Votre code à compléter

Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : notes

        // Appelez la méthode add() ici

        // Déclarez votre variable ici : somme

        // Parcourez avec une boucle for-each ici

        // Calculez ici : moyenne

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> notes = new ArrayList<>();
        notes.add(12);
        notes.add(15);
        notes.add(9);
        notes.add(18);

        int somme = 0;
        for (int note : notes) {
            somme += note;
        }

        double moyenne = (double) somme / notes.size();
        System.out.println("Moyenne : " + moyenne);
    }
}
Console
Moyenne : 13.5
Ma confianceNon évalué
✓ Vérifiable

Exercice 11.5.A Les doublons disparaissent

Ajoutez dans un HashSet<String> les mots rouge, bleu, rouge, vert, bleu. Affichez le nombre d'éléments : il doit valoir 3.

Votre code à compléter

Main.java·Java
import java.util.HashSet;

public class Main {
    public static void main(String[] args) {
        // Créez votre ensemble (HashSet) ici : couleurs

        // Appelez la méthode add() ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.util.HashSet;

public class Main {
    public static void main(String[] args) {
        HashSet<String> couleurs = new HashSet<>();
        couleurs.add("rouge");
        couleurs.add("bleu");
        couleurs.add("rouge");
        couleurs.add("vert");
        couleurs.add("bleu");

        System.out.println(couleurs.size());   // 3
    }
}
Console
3
Ma confianceNon évalué
✓ Vérifiable

Exercice 11.6.A Mon annuaire

Créez un HashMap<String, String> associant Léa → 06 12 34 56 78 et Hugo → 06 98 76 54 32. Affichez le numéro de Hugo.

Votre code à compléter

Main.java·Java
import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        // Créez votre dictionnaire (HashMap) ici : annuaire

        // Appelez la méthode put() ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        HashMap<String, String> annuaire = new HashMap<>();
        annuaire.put("Léa", "06 12 34 56 78");
        annuaire.put("Hugo", "06 98 76 54 32");

        System.out.println("Numéro de Hugo : " + annuaire.get("Hugo"));
    }
}
Console
Numéro de Hugo : 06 98 76 54 32
Ma confianceNon évalué
✓ Vérifiable

Exercice 11.6.B Panier avec quantités

Avec un HashMap<String, Integer>, enregistrez pommes → 3 et poires → 5. Puis affichez chaque ligne sous la forme pommes : 3.

Votre code à compléter

Main.java·Java
import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        // Créez votre dictionnaire (HashMap) ici : panier

        // Appelez la méthode put() ici

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Main.java·Java
import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        HashMap<String, Integer> panier = new HashMap<>();
        panier.put("pommes", 3);
        panier.put("poires", 5);

        for (String fruit : panier.keySet()) {
            System.out.println(fruit + " : " + panier.get(fruit));
        }
    }
}
Console
pommes : 3
poires : 5

L'ordre d'affichage peut varier : c'est normal avec un HashMap.

Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 11

  1. Quelle collection choisir pour une liste sans doublons ?

  2. Quelle collection associe un nom à un numéro de téléphone ?

  3. Pourquoi écrit-on ArrayList<Integer> et non ArrayList<int> ?

  4. Que renvoie liste.get(0) ?

  5. Dans quel ordre un HashMap donne-t-il ses éléments ?

Partie 4 · 4 / 14

Organiser son code et son texte

Chapitres 12 à 13

Chapitre 12 · Organiser son code et son texte

Les méthodes

Objectif. Découper un programme en petits morceaux réutilisables : écrire des méthodes avec des paramètres et une valeur de retour.

Glossaire du chapitre

Méthode
Bloc de code nommé, écrit une fois et appelé autant de fois que nécessaire.
Paramètre
Information que la méthode attend en entrée, avec son type.
Argument
La valeur réelle fournie lors de l'appel.
return
Termine la méthode et renvoie un résultat.
void
Indique que la méthode ne renvoie rien.
Appel
Utilisation d'une méthode en écrivant son nom suivi de parenthèses.
Portée
Zone du programme où une variable existe : une variable créée dans une méthode reste dans cette méthode.

5 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 12.2.A Une méthode de bienvenue

Écrivez une méthode afficherBienvenue() qui affiche Bienvenue à la formation Java !, puis appelez-la deux fois depuis main.

Votre code à compléter

Main.java·Java
public class Main {
    // Méthode à écrire : static void afficherBienvenue()

    public static void main(String[] args) {
        // Complétez ici
    }
}
Voir la solution
Main.java·Java
public class Main {

    static void afficherBienvenue() {
        System.out.println("Bienvenue à la formation Java !");
    }

    public static void main(String[] args) {
        afficherBienvenue();
        afficherBienvenue();
    }
}
Console
Bienvenue à la formation Java !
Bienvenue à la formation Java !
Ma confianceNon évalué
✓ Vérifiable

Exercice 12.3.A Le plus grand de deux

Écrivez static int maximum(int a, int b). Testez avec maximum(7, 12).

Votre code à compléter

Main.java·Java
public class Main {
    // Méthode à écrire : static int maximum(int a, int b)

    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {

    static int maximum(int a, int b) {
        if (a >= b) {
            return a;
        } else {
            return b;
        }
    }

    public static void main(String[] args) {
        System.out.println(maximum(7, 12));   // 12
    }
}
Console
12
Ma confianceNon évalué
✓ Vérifiable

Exercice 12.4.A Le double

Écrivez static int doubler(int n) qui renvoie le double de n. Affichez doubler(21) (résultat : 42).

Votre code à compléter

Main.java·Java
public class Main {
    // Méthode à écrire : static int doubler(int n)

    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {

    static int doubler(int n) {
        return n * 2;
    }

    public static void main(String[] args) {
        System.out.println(doubler(21));
    }
}
Console
42
Ma confianceNon évalué
✓ Vérifiable

Exercice 12.4.B La moyenne d'un tableau

Écrivez static double moyenne(int[] valeurs) qui renvoie la moyenne. Testez avec {10, 12, 17}.

Votre code à compléter

Main.java·Java
public class Main {
    // Méthode à écrire : static double moyenne(int[] valeurs)

    public static void main(String[] args) {
        // Créez votre tableau ici : notes

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {

    static double moyenne(int[] valeurs) {
        int somme = 0;
        for (int valeur : valeurs) {
            somme += valeur;
        }
        return (double) somme / valeurs.length;
    }

    public static void main(String[] args) {
        int[] notes = {10, 12, 17};
        System.out.println(moyenne(notes));   // 13.0
    }
}
Console
13.0
Ma confianceNon évalué
✓ Vérifiable

Exercice 12.5.A Majeur ?

Écrivez static boolean estMajeur(int age). Testez avec 17 et 18.

Votre code à compléter

Main.java·Java
public class Main {
    // Méthode à écrire : static boolean estMajeur(int age)

    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {

    static boolean estMajeur(int age) {
        return age >= 18;
    }

    public static void main(String[] args) {
        System.out.println(estMajeur(17));   // false
        System.out.println(estMajeur(18));   // true
    }
}
Console
false
true
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 12

  1. Que signifie void devant une méthode ?

  2. Que fait return ?

  3. Où écrit-on une méthode ?

  4. Une variable déclarée dans la méthode A est-elle visible dans la méthode B ?

1 gros exercice ou projet

Exercice 12.7.A Le mini-projet

Énoncé du projet · la caisse de la boulangerie

Scénario. Calculer le prix TTC d'un article avec une seule méthode.

  1. static double avecTva(double prixHt) : renvoie le prix TTC (prixHt * 1.055), arrondi au centime.
  2. Dans main : lire le prix HT, appeler la méthode et afficher le résultat.

Réalisez la caisse.

Votre code à compléter

Main.java·Java
import java.util.Scanner;

public class Main {
    // Méthode à écrire : static double avecTva(double prixHt)

    public static void main(String[] args) {
        // Créez le Scanner ici

        // Affichez le résultat ici

        // Lisez la saisie ici : prixHt

        // Affichez le résultat ici

        // Fermez le Scanner ici
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.Scanner;

public class Main {

    static double avecTva(double prixHt) {
        double prixTtc = prixHt * 1.055;
        return Math.round(prixTtc * 100) / 100.0;   // arrondi au centime
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Prix HT (€) : ");
        double prixHt = scanner.nextDouble();

        System.out.println("À payer : " + avecTva(prixHt) + " €");
        scanner.close();
    }
}

⌨ Valeur à saisir

Console
Prix HT (€) : 2
À payer : 2.11 €
Ma confianceNon évalué
✓ Vérifiable

Chapitre 13 · Organiser son code et son texte

String en profondeur

Objectif. Comprendre qu'une String ne change jamais, construire du texte efficacement et le mettre en forme proprement.

Glossaire du chapitre

Immuable
Une String ne change jamais : chaque méthode qui semble la modifier renvoie une nouvelle chaîne.
StringBuilder
Outil pour construire un texte morceau par morceau (append) sans créer une nouvelle chaîne à chaque étape.
String.format / formatted
Insère des valeurs dans un modèle de texte (%s, %d, %.2f).
Spécificateur de format
Code commençant par % qui indique comment afficher une valeur (%d entier, %.2f décimal à 2 chiffres, %s texte).
Bloc de texte
Texte sur plusieurs lignes écrit entre trois guillemets """.
Expression régulière
Motif qui décrit une forme de texte (par exemple « des chiffres »), utilisé par matches ou replaceAll.

4 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 13.2.A Le ticket de caisse

Avec trois tableaux :

  • les noms : "Baguette", "Croissant", "Tarte aux pommes" ;
  • les quantités : 2, 3, 1 ;
  • les prix : 1.10, 1.30, 12.50.

Affichez un ticket aligné en colonnes :

  • le nom sur 18 caractères, à gauche ;
  • la quantité ;
  • le montant de la ligne sur 8 caractères, avec 2 décimales.

Terminez par un trait de 32 tirets, puis le total, aligné sous les montants.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : articles

        // Créez votre tableau ici : quantites

        // Créez votre tableau ici : prix

        // Déclarez votre variable ici : total

        // Écrivez votre boucle ici (for)

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        String[] articles = { "Baguette", "Croissant", "Tarte aux pommes" };
        int[] quantites = { 2, 3, 1 };
        double[] prix = { 1.10, 1.30, 12.50 };

        double total = 0;
        for (int i = 0; i < articles.length; i++) {
            double ligne = quantites[i] * prix[i];
            total += ligne;
            System.out.printf("%-18s x%d %8.2f €%n", articles[i], quantites[i], ligne);
        }
        System.out.println("--------------------------------");
        System.out.printf("%-21s %8.2f €%n", "TOTAL", total);
    }
}
Console
Baguette           x2     2,20 €
Croissant          x3     3,90 €
Tarte aux pommes   x1    12,50 €
--------------------------------
TOTAL                    18,60 €

La ligne TOTAL occupe 21 caractères, comme %-18s x%d (18 + 2 + 1) : les montants tombent dans la même colonne.

Ma confianceNon évalué
✓ Vérifiable

Exercice 13.2.B Une ligne de nombres

Écrivez une méthode static String ligne(int n) qui renvoie "1-2-3-…-n", sans tiret à la fin, en utilisant un StringBuilder. Testez avec 5, 10 et 1.

Votre code à compléter

Main.java·Java
public class Main {
    // Méthode à écrire : static String ligne(int n)

    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    static String ligne(int n) {
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= n; i++) {
            if (i > 1) {
                sb.append("-");   // un tiret avant chaque nombre, sauf le premier
            }
            sb.append(i);
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        System.out.println(ligne(5));
        System.out.println(ligne(10));
        System.out.println(ligne(1));
    }
}
Console
1-2-3-4-5
1-2-3-4-5-6-7-8-9-10
1
Ma confianceNon évalué
✓ Vérifiable

Exercice 13.4.A Le menu en bloc de texte

Affichez un menu de trois choix :

  • encadré de traits = ;
  • avec une ligne d'accueil Bonjour Hugo, vous avez 120 points.

Le nom et les points sont dans des variables : utilisez un bloc de texte et formatted.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : nom

        // Déclarez votre variable ici : points

        // Complétez ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        String nom = "Hugo";
        int points = 120;

        String menu = """
                ==================================
                Bonjour %s, vous avez %d points
                ==================================
                  1. Commander
                  2. Voir mes points
                  3. Quitter
                """.formatted(nom, points);

        System.out.print(menu);
    }
}
Console
==================================
Bonjour Hugo, vous avez 120 points
==================================
  1. Commander
  2. Voir mes points
  3. Quitter
Ma confianceNon évalué
✓ Vérifiable

Exercice 13.5.A Palindrome ?

Écrivez static boolean estPalindrome(String mot) :

  • elle renvoie vrai si le mot se lit pareil dans les deux sens, sans tenir compte des majuscules ;
  • utilisez reverse().

Testez avec "Kayak", "radar" et "Java".

Votre code à compléter

Main.java·Java
public class Main {
    // Méthode à écrire : static boolean estPalindrome(String mot)

    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    static boolean estPalindrome(String mot) {
        String envers = new StringBuilder(mot).reverse().toString();
        return mot.equalsIgnoreCase(envers);
    }

    public static void main(String[] args) {
        System.out.println(estPalindrome("Kayak"));
        System.out.println(estPalindrome("radar"));
        System.out.println(estPalindrome("Java"));
    }
}
Console
true
true
false

reverse() appartient à StringBuilder, pas à String : on passe par lui, puis on revient à une String avec toString().

Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 13

  1. Que vaut nom après String nom = "léa"; nom.toUpperCase(); ?

  2. Pourquoi utiliser un StringBuilder dans une boucle ?

  3. Que donne String.format("[%5d]", 42) ?

  4. Sur une machine en français, "%.2f".formatted(2.5) donne…

  5. Que renvoie " ".isBlank() ?

1 gros exercice ou projet

Exercice 13.5.B Gros exercice : le ticket de caisse bien aligné (StringBuilder + format)

Exercice de synthèse du chapitre. On génère un ticket de caisse proprement aligné, en réunissant StringBuilder (construire le texte) et String.format (aligner les colonnes et les prix).

On part de ces articles (nom ; prix unitaire ; quantité), à ranger dans trois tableaux (String[], double[], int[]) :

Console
Baguette   1.10  3
Croissant  1.20  2
Gâteau     18.50 1

Construisez, avec un seul StringBuilder, un ticket qui affiche chaque ligne alignée (nom, qté × prix, sous-total), puis le total. Les montants doivent avoir 2 décimales.

Déroulé attendu :

Console
===== BOULANGERIE =====
Baguette   3 x 1,10 = 3,30 €
Croissant  2 x 1,20 = 2,40 €
Gâteau     1 x 18,50 = 18,50 €
------------------------
TOTAL      24,20 €

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : noms

        // Créez votre tableau ici : prix

        // Créez votre tableau ici : quantites

        // Créez votre objet ici : ticket

        // Appelez la méthode append() ici

        // Déclarez votre variable ici : total

        // Écrivez votre boucle ici (for)

        // Appelez la méthode append() ici

        // Affichez le résultat ici
    }
}
Voir la solution

On construit tout le ticket dans un StringBuilder (plutôt que des dizaines de +), et chaque ligne est mise en forme avec String.format : %-10s cale le nom à gauche sur 10 colonnes, %d affiche un entier et %.2f force 2 décimales pour les montants. Résultat : des colonnes parfaitement alignées.

Main.java·Java
public class Main {
    public static void main(String[] args) {
        String[] noms = {"Baguette", "Croissant", "Gâteau"};
        double[] prix = {1.10, 1.20, 18.50};
        int[] quantites = {3, 2, 1};

        StringBuilder ticket = new StringBuilder();
        ticket.append("===== BOULANGERIE =====\n");

        double total = 0;
        for (int i = 0; i < noms.length; i++) {
            double sousTotal = prix[i] * quantites[i];
            total += sousTotal;
            ticket.append(String.format("%-10s %d x %.2f = %.2f €%n",
                noms[i], quantites[i], prix[i], sousTotal));
        }
        ticket.append("------------------------\n");
        ticket.append(String.format("%-10s %.2f €%n", "TOTAL", total));

        System.out.print(ticket);
    }
}
Ma confianceNon évalué
✓ Vérifiable

Partie 5 · 5 / 14

Les classes et les objets

Chapitres 14 à 17

Chapitre 14 · Les classes et les objets

Classes et objets

Objectif. Comprendre la différence entre une classe (le plan) et un objet (la chose construite), créer ses propres classes avec des attributs, un constructeur et des méthodes.

Glossaire du chapitre

Classe
Le plan, le modèle d'un objet.
Objet (instance)
Une réalisation concrète d'une classe, créée avec new.
Attribut
Une donnée stockée dans l'objet (pseudo, niveau).
Méthode d'objet
Une action que l'objet sait faire.
Constructeur
Méthode spéciale appelée à la création de l'objet : même nom que la classe, pas de type de retour.
new
Mot-clé qui construit un objet.
this
Désigne l'objet courant (this.pseudo est l'attribut de cet objet).
Surcharge
Plusieurs constructeurs ou méthodes de même nom mais de paramètres différents.

8 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 14.2.A Une classe Chat

Créez une classe Chat avec les attributs nom (String) et age (int), et un constructeur qui les initialise. Dans main, créez un chat Félix de 3 ans et affichez son nom.

Votre code à compléter

Chat.java·Java
public class Chat {
    // Constructeur : Chat(String nom, int age)
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : felix

        // Affichez le résultat ici
    }
}
Voir la solution
Chat.java·Java
public class Chat {
    String nom;
    int age;

    Chat(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Chat felix = new Chat("Félix", 3);

        System.out.println(felix.nom);
    }
}
Console
Félix
Ma confianceNon évalué
✓ Vérifiable

Exercice 14.3.A Deux objets indépendants

Dans main, créez deux chats (Félix, 3 ans et Luna, 5 ans). Faites miauler les deux, puis affichez lequel est le plus âgé.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : felix

        // Créez votre objet ici : luna

        // Appelez la méthode miauler() ici

        // Écrivez votre condition ici (if / else if / else)
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Chat felix = new Chat("Félix", 3);
        Chat luna = new Chat("Luna", 5);

        felix.miauler();
        luna.miauler();

        if (felix.age > luna.age) {
            System.out.println(felix.nom + " est le plus âgé.");
        } else if (luna.age > felix.age) {
            System.out.println(luna.nom + " est le plus âgé.");
        } else {
            System.out.println("Ils ont le même âge.");
        }
    }
}
Console
Félix dit : Miaou !
Luna dit : Miaou !
Luna est le plus âgé.
Ma confianceNon évalué
Non vérifiable

Exercice 14.3.B Un compteur

Créez une classe Compteur :

  • un attribut valeur (un int, qui vaut 0 au départ) ;
  • trois méthodes :
    • incrementer() ajoute 1 ;
    • reinitialiser() remet à 0 ;
    • afficher() écrit Compteur : <valeur>.

Dans main, créez deux compteurs, incrémentez le premier trois fois et le second une fois, puis affichez-les :

Console
Compteur : 3
Compteur : 1

Votre code à compléter

Compteur.java·Java
public class Compteur {
    // Méthode à écrire : void incrementer()
    // Méthode à écrire : void reinitialiser()
    // Méthode à écrire : void afficher()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : a

        // Créez votre objet ici : b

        // Appelez la méthode incrementer() ici

        // Appelez la méthode afficher() ici
    }
}
Voir la solution
Compteur.java·Java
public class Compteur {
    int valeur = 0;

    void incrementer() {
        valeur++;
    }

    void reinitialiser() {
        valeur = 0;
    }

    void afficher() {
        System.out.println("Compteur : " + valeur);
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Compteur a = new Compteur();
        Compteur b = new Compteur();

        a.incrementer();
        a.incrementer();
        a.incrementer();
        b.incrementer();

        a.afficher();
        b.afficher();
    }
}
Console
Compteur : 3
Compteur : 1

Chaque objet a son valeur : modifier a ne touche pas b. Pas besoin de constructeur ici, car l'attribut reçoit sa valeur de départ à sa déclaration (int valeur = 0;).

Ma confianceNon évalué
✓ Vérifiable

Exercice 14.3.C Une variable, deux noms : que s'affiche-t-il ?

Lisez ce programme sans le lancer, puis écrivez ce qu'il affiche. Vérifiez ensuite en le lançant dans IntelliJ.

Chaton.java·Java
public class Chaton {
    String nom;
    int age;

    Chaton(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Chaton a = new Chaton("Mina", 1);
        Chaton b = a;
        Chaton c = new Chaton("Mina", 1);

        b.age = 5;

        System.out.println(a.age);
        System.out.println(b.age);
        System.out.println(c.age);
    }
}
Voir la solution
Console
5
5
1

Chaton b = a; ne crée pas un deuxième chaton : b et a sont deux noms pour le même objet (deux étiquettes sur la même boîte). Modifier b.age modifie donc aussi a.age. c, créé avec new, est un autre objet : il garde son age de 1. On reverra ce piège au chapitre 27.

Ma confianceNon évalué
Non vérifiable

Exercice 14.4.A Vieillir

Ajoutez à Chat une méthode feterAnniversaire() qui augmente l'âge de 1. Appelez-la deux fois sur Félix puis affichez son âge (5).

Votre code à compléter

Chat.java·Java
public class Chat {
    // Constructeur : Chat(String nom, int age)
    // Méthode à écrire : void feterAnniversaire()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : felix

        // Appelez la méthode feterAnniversaire() ici

        // Affichez le résultat ici
    }
}
Voir la solution
Chat.java·Java
public class Chat {
    String nom;
    int age;

    Chat(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }

    void feterAnniversaire() {
        age++;
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Chat felix = new Chat("Félix", 3);

        felix.feterAnniversaire();
        felix.feterAnniversaire();

        System.out.println(felix.age);   // 5
    }
}
Console
5
Ma confianceNon évalué
✓ Vérifiable

Exercice 14.5.A Une méthode qui parle

Ajoutez à Chat une méthode miauler() qui affiche Félix dit : Miaou ! (avec le nom du chat). Appelez-la depuis main.

Votre code à compléter

Chat.java·Java
public class Chat {
    // Constructeur : Chat(String nom, int age)
    // Méthode à écrire : void miauler()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : felix

        // Appelez la méthode miauler() ici
    }
}
Voir la solution
Chat.java·Java
public class Chat {
    String nom;
    int age;

    Chat(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }

    void miauler() {
        System.out.println(nom + " dit : Miaou !");
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Chat felix = new Chat("Félix", 3);
        felix.miauler();
    }
}
Console
Félix dit : Miaou !
Ma confianceNon évalué
✓ Vérifiable

Exercice 14.5.B Un rectangle

Créez une classe Rectangle :

  • deux attributs largeur et longueur (des double) ;
  • un constructeur qui les initialise ;
  • deux méthodes aire() et perimetre(), qui renvoient un double.

Dans main, créez un rectangle de 4 sur 5 et affichez son aire et son périmètre :

Console
Aire : 20.0
Périmètre : 18.0

Votre code à compléter

Rectangle.java·Java
public class Rectangle {
    // Constructeur : Rectangle(double largeur, double longueur)
    // Méthode à écrire : double aire()
    // Méthode à écrire : double perimetre()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : r

        // Affichez le résultat ici
    }
}
Voir la solution
Rectangle.java·Java
public class Rectangle {
    double largeur;
    double longueur;

    Rectangle(double largeur, double longueur) {
        this.largeur = largeur;
        this.longueur = longueur;
    }

    double aire() {
        return largeur * longueur;
    }

    double perimetre() {
        return 2 * (largeur + longueur);
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Rectangle r = new Rectangle(4, 5);

        System.out.println("Aire : " + r.aire());
        System.out.println("Périmètre : " + r.perimetre());
    }
}
Console
Aire : 20.0
Périmètre : 18.0
Ma confianceNon évalué
✓ Vérifiable

Exercice 14.6.A Un élève avec deux constructeurs

Créez une classe Eleve :

  • deux attributs : prenom (String) et age (int) ;
  • deux constructeurs :
    • un complet, Eleve(String prenom, int age) ;
    • un pratique, Eleve(String prenom), qui donne 18 ans par défaut ;
  • une méthode sePresenter() qui affiche Je suis <prenom>, <age> ans.

Créez Camille (18 ans par défaut) et Hugo (22 ans) :

Console
Je suis Camille, 18 ans.
Je suis Hugo, 22 ans.

Votre code à compléter

Eleve.java·Java
public class Eleve {
    // Constructeur : Eleve(String prenom, int age)
    // Constructeur : Eleve(String prenom)
    // Méthode à écrire : void sePresenter()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : camille

        // Créez votre objet ici : hugo

        // Appelez la méthode sePresenter() ici
    }
}
Voir la solution
Eleve.java·Java
public class Eleve {
    String prenom;
    int age;

    // Constructeur n°1 : le complet
    Eleve(String prenom, int age) {
        this.prenom = prenom;
        this.age = age;
    }

    // Constructeur n°2 : le pratique, avec une valeur par défaut
    Eleve(String prenom) {
        this.prenom = prenom;
        this.age = 18;
    }

    void sePresenter() {
        System.out.println("Je suis " + prenom + ", " + age + " ans.");
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Eleve camille = new Eleve("Camille");
        Eleve hugo = new Eleve("Hugo", 22);

        camille.sePresenter();
        hugo.sePresenter();
    }
}
Console
Je suis Camille, 18 ans.
Je suis Hugo, 22 ans.
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 14

  1. Quelle est la différence entre une classe et un objet ?

  2. Quel mot-clé crée un objet ?

  3. Que désigne this dans un constructeur ?

  4. Un constructeur a…

Chapitre 15 · Les classes et les objets

L'encapsulation

Objectif. Protéger les données d'un objet avec private, et y accéder de façon contrôlée grâce aux getters et aux setters.

Glossaire du chapitre

Encapsulation
Cacher les données d'un objet et n'autoriser leur modification que par des méthodes qui vérifient.
private
Visible uniquement dans la classe.
public
Visible partout.
protected
Visible dans la classe, ses classes filles et son package.
Getter
Méthode qui lit un attribut privé (getSolde()).
Setter
Méthode qui modifie un attribut en vérifiant la valeur (setNombrePages()).

6 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 15.3.A Une carte cadeau

Créez une classe CarteCadeau :

  • un attribut privé solde, dont la valeur de départ est donnée au constructeur ;
  • un getter getSolde() ;
  • une méthode depenser(double montant) qui retire le montant seulement s'il reste assez de solde.

Dans main, créez une carte de 50 €.

Votre code à compléter

CarteCadeau.java·Java
public class CarteCadeau {
    // Attributs : solde
    // Constructeur : CarteCadeau(double solde)
    // Méthode à écrire : public double getSolde()
    // Méthode à écrire : public void depenser(double montant)
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : carte

        // Appelez la méthode depenser() ici

        // Affichez le résultat ici
    }
}
Voir la solution
CarteCadeau.java·Java
public class CarteCadeau {
    private double solde;

    public CarteCadeau(double solde) {
        this.solde = solde;
    }

    public double getSolde() {
        return solde;
    }

    public void depenser(double montant) {
        if (montant > 0 && montant <= solde) {
            solde -= montant;
        } else {
            System.out.println("Solde insuffisant.");
        }
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        CarteCadeau carte = new CarteCadeau(50);

        carte.depenser(20);
        carte.depenser(40);   // Solde insuffisant.

        System.out.println(carte.getSolde());   // 30.0
    }
}
Console
Solde insuffisant.
30.0
Ma confianceNon évalué
✓ Vérifiable

Exercice 15.3.B Le setter du livre

Reprenez Livre. Ajoutez un setter setTitre(String titre) qui refuse un titre vide (titre.isEmpty()).

Voir la solution
Java
public void setTitre(String titre) {
    if (!titre.isEmpty()) {
        this.titre = titre;
    } else {
        System.out.println("Titre invalide.");
    }
}
Ma confianceNon évalué
Non vérifiable

Exercice 15.4.A Le thermostat

Créez une classe Thermostat :

  • un attribut privé temperature (un int, 19 au départ) ;
  • un getter ;
  • un setter setTemperature(int temperature) qui n'accepte que les valeurs entre 5 et 30 ; sinon, il affiche Température refusée : <valeur> et ne change rien.

Testez avec 22, puis 50 :

Console
Température : 22
Température refusée : 50
Température : 22

Votre code à compléter

Thermostat.java·Java
public class Thermostat {
    // Attributs : temperature
    // Méthode à écrire : public int getTemperature()
    // Méthode à écrire : public void setTemperature(int temperature)
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : salon

        // Appelez la méthode setTemperature() ici

        // Affichez le résultat ici

        // Appelez la méthode setTemperature() ici

        // Affichez le résultat ici
    }
}
Voir la solution
Thermostat.java·Java
public class Thermostat {
    private int temperature = 19;

    public int getTemperature() {
        return temperature;
    }

    public void setTemperature(int temperature) {
        if (temperature >= 5 && temperature <= 30) {
            this.temperature = temperature;
        } else {
            System.out.println("Température refusée : " + temperature);
        }
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Thermostat salon = new Thermostat();

        salon.setTemperature(22);
        System.out.println("Température : " + salon.getTemperature());

        salon.setTemperature(50);
        System.out.println("Température : " + salon.getTemperature());
    }
}
Console
Température : 22
Température refusée : 50
Température : 22
Ma confianceNon évalué
✓ Vérifiable

Exercice 15.5.A Tester le compte

Avec CompteBancaire, créez un compte de 100 €, déposez 50, retirez 30, tentez de retirer 500, puis affichez le solde (120.0).

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : compte

        // Appelez la méthode deposer() ici

        // Appelez la méthode retirer() ici

        // Déclarez votre variable ici : ok

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        CompteBancaire compte = new CompteBancaire("Léa", 100);

        compte.deposer(50);
        compte.retirer(30);

        boolean ok = compte.retirer(500);
        System.out.println("Retrait de 500 accepté ? " + ok);   // false

        System.out.println("Solde : " + compte.getSolde());     // 120.0
    }
}
Console
Retrait de 500 accepté ? false
Solde : 120.0
Ma confianceNon évalué
Non vérifiable

Exercice 15.5.B Un livret d'épargne

Créez une classe Livret :

  • un attribut privé solde (un double), reçu par le constructeur ;
  • un getter ;
  • deux méthodes deposer(double montant) et retirer(double montant) ;
  • pas de setter : le solde ne change que par ces deux méthodes.

Deux règles :

  • un montant négatif ou nul est refusé (Montant invalide.) ;
  • on ne peut pas retirer plus que le solde (Solde insuffisant.).

Testez : livret de 100 €, dépôt de 50, retrait de 500, retrait de 30.

Console
Solde insuffisant.
Solde : 120.0 €

Votre code à compléter

Livret.java·Java
public class Livret {
    // Attributs : solde
    // Constructeur : Livret(double solde)
    // Méthode à écrire : public double getSolde()
    // Méthode à écrire : public void deposer(double montant)
    // Méthode à écrire : public void retirer(double montant)
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : livret

        // Appelez la méthode deposer() ici

        // Appelez la méthode retirer() ici

        // Affichez le résultat ici
    }
}
Voir la solution
Livret.java·Java
public class Livret {
    private double solde;

    public Livret(double solde) {
        this.solde = solde;
    }

    public double getSolde() {
        return solde;
    }

    public void deposer(double montant) {
        if (montant <= 0) {
            System.out.println("Montant invalide.");
            return;
        }
        solde += montant;
    }

    public void retirer(double montant) {
        if (montant <= 0) {
            System.out.println("Montant invalide.");
        } else if (montant > solde) {
            System.out.println("Solde insuffisant.");
        } else {
            solde -= montant;
        }
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Livret livret = new Livret(100);

        livret.deposer(50);
        livret.retirer(500);
        livret.retirer(30);

        System.out.println("Solde : " + livret.getSolde() + " €");
    }
}
Console
Solde insuffisant.
Solde : 120.0 €
Ma confianceNon évalué
✓ Vérifiable

Exercice 15.5.C Un mot de passe bien gardé

Créez une classe Compte :

  • un attribut privé motDePasse (un String), sans getter ;
  • un constructeur qui reçoit le mot de passe ;
  • une méthode boolean verifier(String essai) qui renvoie true si l'essai est égal au mot de passe.

Dans main, créez un compte avec "secret", puis testez "secret" et "123456".

Pourquoi n'y a-t-il pas de getMotDePasse() ici ?

Console
true
false

Votre code à compléter

Compte.java·Java
public class Compte {
    // Attributs : motDePasse
    // Constructeur : Compte(String motDePasse)
    // Méthode à écrire : public boolean verifier(String essai)
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : compte

        // Affichez le résultat ici
    }
}
Voir la solution
Compte.java·Java
public class Compte {
    private String motDePasse;

    public Compte(String motDePasse) {
        this.motDePasse = motDePasse;
    }

    public boolean verifier(String essai) {
        return motDePasse.equals(essai);
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Compte compte = new Compte("secret");

        System.out.println(compte.verifier("secret"));
        System.out.println(compte.verifier("123456"));
    }
}
Console
true
false

Un getter rendrait le mot de passe lisible par n'importe quelle classe. En ne proposant que verifier, la classe garde son secret : on peut tester un mot de passe sans jamais le lire. C'est tout l'intérêt de l'encapsulation : on décide ce qu'on expose. Notez aussi equals pour comparer des textes (section 7.5).

Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 15

  1. Pourquoi déclarer un attribut private ?

  2. À quoi sert un getter ?

  3. Quel est l'intérêt principal d'un setter ?

  4. Que signifie private ?

Chapitre 16 · Les classes et les objets

Le mot-clé static

Objectif. Comprendre ce qui appartient à la classe (static) et ce qui appartient à chaque objet, et pourquoi main est static.

Glossaire du chapitre

static
Appartient à la classe et non à un objet : un seul exemplaire partagé.
final
Ne peut plus être modifié.
Constante
Valeur fixe, déclarée static final, nommée en MAJUSCULES (TAUX_TVA).
Nombre magique
Valeur écrite en dur sans explication (18). On le remplace par une constante nommée.
Méthode statique
Méthode appelée avec le nom de la classe (Math.sqrt(16)), sans objet.
args
Tableau de String qui contient les arguments passés au programme au lancement.

4 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 16.2.A Compter les visiteurs

Créez une classe Visiteur avec un compteur static qui augmente à chaque nouvel objet. Créez 4 visiteurs et affichez le compteur (4).

Votre code à compléter

Visiteur.java·Java
public class Visiteur {
    // Attributs : total, nom
    // Constructeur : Visiteur(String nom)
    // Méthode à écrire : public static int getTotal()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez vos objets ici

        // Affichez le résultat ici
    }
}
Voir la solution
Visiteur.java·Java
public class Visiteur {
    private static int total = 0;
    private String nom;

    public Visiteur(String nom) {
        this.nom = nom;
        total++;
    }

    public static int getTotal() {
        return total;
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        new Visiteur("Léa");
        new Visiteur("Hugo");
        new Visiteur("Inès");
        new Visiteur("Noé");

        System.out.println(Visiteur.getTotal());   // 4
    }
}
Console
4
Ma confianceNon évalué
✓ Vérifiable

Exercice 16.3.A Une méthode utilitaire

Dans une classe Outils, écrivez public static int carre(int n) qui renvoie n * n. Affichez Outils.carre(7) (49).

Votre code à compléter

Outils.java·Java
public class Outils {
    // Méthode à écrire : public static int carre(int n)
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Outils.java·Java
public class Outils {
    public static int carre(int n) {
        return n * n;
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        System.out.println(Outils.carre(7));   // 49
    }
}
Console
49
Ma confianceNon évalué
✓ Vérifiable

Exercice 16.3.B Utiliser Math

Avec la classe Math, calculez et affichez l'aire d'un disque de rayon 3 (Math.PI * rayon * rayon).

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : rayon

        // Calculez ici : aire

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        double rayon = 3;
        double aire = Math.PI * rayon * rayon;

        System.out.println("Aire : " + aire);
    }
}
Console
Aire : 28.274333882308138
Ma confianceNon évalué
✓ Vérifiable

Exercice 16.5.A Les constantes d'un jeu

Créez une classe Jeu avec deux constantes :

  • VIES_MAX : 3 ;
  • POINTS_PAR_PIECE : 10.

Dans main, un joueur ramasse 7 pièces : calculez son score avec la constante et affichez Score : 70, puis Vies : 3.

Votre code à compléter

Jeu.java·Java
public class Jeu {
    // Attributs : VIES_MAX, POINTS_PAR_PIECE
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : pieces

        // Calculez ici : score

        // Affichez le résultat ici
    }
}
Voir la solution
Jeu.java·Java
public class Jeu {
    public static final int VIES_MAX = 3;
    public static final int POINTS_PAR_PIECE = 10;
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        int pieces = 7;
        int score = pieces * Jeu.POINTS_PAR_PIECE;

        System.out.println("Score : " + score);
        System.out.println("Vies : " + Jeu.VIES_MAX);
    }
}
Console
Score : 70
Vies : 3
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 16

  1. À qui appartient un attribut static ?

  2. Dans la forme classique, pourquoi main est-il static ?

  3. Comment appelle-t-on une méthode statique sqrt de la classe Math ?

  4. Que signifie static final ?

  5. Que contient le tableau args quand on lance le programme sans rien ajouter ?

Chapitre 17 · Les classes et les objets

Packages et import

Objectif. Ranger ses classes dans des packages, importer celles des autres et comprendre qui a le droit de voir quoi.

Glossaire du chapitre

Package
Dossier de rangement des classes, comme l'adresse postale d'une classe.
package
Première ligne d'un fichier : elle déclare le package de la classe. Le dossier doit correspondre.
import
Permet d'utiliser une classe d'un autre package sans écrire son nom complet.
Nom complet
Nom de la classe précédé de son package (java.util.ArrayList).
import static
Importe un membre static pour l'utiliser sans nom de classe.
Accès « package »
Sans mot-clé de visibilité, un membre n'est visible que par les classes du même package.

3 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 17.2.A Ranger une bibliothèque

Créez deux classes dans le package fr.bibliotheque.modele :

  1. Livre :
    • un titre ;
    • son getter.
  2. Adherent :
    • un nom ;
    • une méthode emprunter(Livre livre) qui affiche Léa emprunte « Le Petit Prince ».

Mettez le Main dans le package fr.bibliotheque.

Votre code à compléter

Livre.java·Java
public class Livre {
    // Attributs : titre
    // Constructeur : Livre(String titre)
    // Méthode à écrire : public String getTitre()
}
Adherent.java·Java
public class Adherent {
    // Attributs : nom
    // Constructeur : Adherent(String nom)
    // Méthode à écrire : public void emprunter(Livre livre)
}
Main.java·Java

import fr.bibliotheque.modele.Adherent;
import fr.bibliotheque.modele.Livre;

public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : lea

        // Appelez la méthode emprunter() ici
    }
}
Voir la solution

src/fr/bibliotheque/modele/Livre.java :

Livre.java·Java
package fr.bibliotheque.modele;

public class Livre {
    private final String titre;

    public Livre(String titre) {
        this.titre = titre;
    }

    public String getTitre() {
        return titre;
    }
}

src/fr/bibliotheque/modele/Adherent.java (pas d'import : Livre est dans le même package) :

Adherent.java·Java
package fr.bibliotheque.modele;

public class Adherent {
    private final String nom;

    public Adherent(String nom) {
        this.nom = nom;
    }

    public void emprunter(Livre livre) {
        System.out.println(nom + " emprunte « " + livre.getTitre() + " »");
    }
}

src/fr/bibliotheque/Main.java :

Main.java·Java
package fr.bibliotheque;

import fr.bibliotheque.modele.Adherent;
import fr.bibliotheque.modele.Livre;

public class Main {
    public static void main(String[] args) {
        Adherent lea = new Adherent("Léa");
        lea.emprunter(new Livre("Le Petit Prince"));
    }
}
Console
Léa emprunte « Le Petit Prince »
Ma confianceNon évalué
✓ Vérifiable

Exercice 17.3.A Deux Date

Ce programme ne compile pas. Lisez le message, puis corrigez-le pour utiliser la Date de java.util, sans retirer aucun des deux imports *.

Main.java·Java
import java.sql.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Date aujourdhui = new Date();
        System.out.println(aujourdhui != null);
    }
}
Voir la solution

Le compilateur répond :

Console
Main.java:6: error: reference to Date is ambiguous
        Date aujourdhui = new Date();
        ^
  both class java.util.Date in java.util and class java.sql.Date in java.sql match
Main.java:6: error: reference to Date is ambiguous
        Date aujourdhui = new Date();
                              ^
  both class java.util.Date in java.util and class java.sql.Date in java.sql match
2 errors

Un import précis l'emporte sur les imports * :

Main.java·Java
import java.sql.*;
import java.util.*;
import java.util.Date;   // c'est cette Date-là qu'on veut

public class Main {
    public static void main(String[] args) {
        Date aujourdhui = new Date();
        System.out.println(aujourdhui != null);
    }
}
Console
true
Ma confianceNon évalué
✓ Vérifiable

Exercice 17.4.A Pythagore avec import static

Calculez l'hypoténuse d'un triangle rectangle de côtés 3 et 4 (√(a² + b²)), en écrivant sqrt et pow sans Math. devant.

Votre code à compléter

Main.java·Java
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;

public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : a

        // Déclarez votre variable ici : b

        // Calculez ici : c

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;

public class Main {
    public static void main(String[] args) {
        double a = 3;
        double b = 4;
        double c = sqrt(pow(a, 2) + pow(b, 2));
        System.out.println("Hypoténuse : " + c);
    }
}
Console
Hypoténuse : 5.0
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 17

  1. Où doit se trouver la classe déclarée package fr.boulangerie.caisse; ?

  2. Quelle classe s'utilise sans import ?

  3. Avec import java.awt.*; et import java.util.*;, que donne List<String> noms; ?

  4. À quoi sert import static java.lang.Math.max; ?

  5. Une classe déclarée class Recette, sans public, est visible…

  6. En Java, un membre protected est visible…

Partie 6 · 6 / 14

Les dates

Chapitre 18

Chapitre 18 · Les dates

Les dates

Objectif. Créer une date, ajouter ou retirer des jours, comparer deux dates et les afficher au format français, avec LocalDate.

Glossaire du chapitre

LocalDate
Une date sans heure (2026-10-19). On la crée avec LocalDate.of(année, mois, jour) ou LocalDate.now().
java.time
Le package de Java qui contient les dates et les heures (LocalDate, LocalTime, LocalDateTime…).
Immuable (dates)
Une date ne change jamais : plusDays(21) renvoie une nouvelle date qu'il faut ranger dans une variable.
plusDays / minusMonths
Calculent une nouvelle date en ajoutant ou en retirant des jours, des mois, des années.
isBefore / isAfter
Comparent deux dates (< et > ne marchent que sur les nombres).
DateTimeFormatter
Décrit la forme d'une date pour l'afficher : dd/MM/yyyy. MM est le mois, mm les minutes.

3 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 18.2.A La date de retour

Un livre est emprunté le 2026-10-19 pour 21 jours. Affichez À rendre le : 2026-11-09.

Votre code à compléter

Main.java·Java
import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : emprunt

        // Déclarez votre variable ici : retour

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        LocalDate emprunt = LocalDate.of(2026, 10, 19);
        LocalDate retour = emprunt.plusDays(21);

        System.out.println("À rendre le : " + retour);
    }
}
Console
À rendre le : 2026-11-09
Ma confianceNon évalué
✓ Vérifiable

Exercice 18.3.A Le livre est-il en retard ?

La date limite est le 2026-11-09 et nous sommes le 2026-11-12. Affichez En retard si la date du jour est après la limite, sinon Dans les temps.

Votre code à compléter

Main.java·Java
import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : limite

        // Déclarez votre variable ici : aujourdhui

        // Écrivez votre condition ici (if / else)
    }
}
Voir la solution
Main.java·Java
import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        LocalDate limite = LocalDate.of(2026, 11, 9);
        LocalDate aujourdhui = LocalDate.of(2026, 11, 12);

        if (aujourdhui.isAfter(limite)) {
            System.out.println("En retard");
        } else {
            System.out.println("Dans les temps");
        }
    }
}
Console
En retard
Ma confianceNon évalué
✓ Vérifiable

Exercice 18.4.A La rentrée en français

La rentrée est le 2026-09-01. Affichez Rentrée : 01/09/2026, puis, sur la ligne suivante, Vacances : 21/10/2026 pour la date 2026-10-21.

Votre code à compléter

Main.java·Java
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        // Calculez ici : francais

        // Déclarez votre variable ici : rentree

        // Déclarez votre variable ici : vacances

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter francais = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        LocalDate rentree = LocalDate.of(2026, 9, 1);
        LocalDate vacances = LocalDate.of(2026, 10, 21);

        System.out.println("Rentrée : " + rentree.format(francais));
        System.out.println("Vacances : " + vacances.format(francais));
    }
}
Console
Rentrée : 01/09/2026
Vacances : 21/10/2026
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 18

  1. Que contient emprunt après emprunt.plusDays(21); ?

  2. Quel mois désigne LocalDate.of(2026, 9, 1) ?

  3. Comment savoir si la date a est avant la date b ?

  4. Quel motif affiche la date 01/09/2026 ?

  5. Quel type choisir pour une date de naissance ?

Partie 7 · 7 / 14

Héritage, classes abstraites et interfaces

Chapitres 19 à 21

Chapitre 19 · Héritage, classes abstraites et interfaces

L'héritage

Objectif. Éviter de recopier du code en créant des classes « filles » qui reprennent les caractéristiques d'une classe « mère », avec extends, super, @Override et le polymorphisme.

Glossaire du chapitre

Héritage
Une classe fille reprend les attributs et méthodes d'une classe mère.
extends
Exprime l'héritage : Chien extends Animal.
Classe mère / fille
La mère est la classe dont on hérite, la fille celle qui hérite.
super
Appelle le constructeur (ou une méthode) de la classe mère.
@Override
Signale la redéfinition d'une méthode héritée ; le compilateur vérifie qu'elle existe.
Redéfinition
La fille remplace le comportement d'une méthode héritée.
Polymorphisme
Une variable de la classe mère peut contenir un objet fille : c'est la version de l'objet réel qui s'exécute.
« Est-un »
Relation d'héritage : un chien est un animal.

8 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 19.2.A Animal et Oiseau

Ajoutez une classe Oiseau qui hérite d'Animal et redéfinit crier() pour afficher <nom> chante : Cui-cui !. Testez avec Piou, 1 an.

Votre code à compléter

Oiseau.java·Java
public class Oiseau extends Animal {
    // Constructeur : Oiseau(String nom, int age)
    // Méthode à écrire : public void crier()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : piou

        // Appelez la méthode crier() ici
    }
}
Voir la solution
Oiseau.java·Java
public class Oiseau extends Animal {

    public Oiseau(String nom, int age) {
        super(nom, age);
    }

    @Override
    public void crier() {
        System.out.println(nom + " chante : Cui-cui !");
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Oiseau piou = new Oiseau("Piou", 1);
        piou.crier();
    }
}
Console
Piou chante : Cui-cui !
Ma confianceNon évalué
✓ Vérifiable

Exercice 19.3.A Hériter d'une méthode

Sans rien écrire de plus dans Oiseau, appelez piou.manger(). Que se passe-t-il, et pourquoi ?

Voir la solution

Le programme affiche Piou mange.. Oiseau hérite de toutes les méthodes publiques d'Animal, dont manger().

Ma confianceNon évalué
Non vérifiable

Exercice 19.3.B Compléter la méthode de la mère avec super

Créez deux classes :

  1. Personne :
    • un attribut prenom ;
    • une méthode sePresenter() qui affiche Je m'appelle <prenom>.
  2. Etudiant, qui hérite de Personne :
    • un attribut filiere ;
    • sePresenter() redéfinie pour d'abord appeler la version de la mère (super.sePresenter()), puis afficher J'étudie : <filiere>.

Testez avec Inès, filière Informatique :

Console
Je m'appelle Inès.
J'étudie : Informatique.

Votre code à compléter

Personne.java·Java
public class Personne {
    // Attributs : prenom
    // Constructeur : Personne(String prenom)
    // Méthode à écrire : public void sePresenter()
}
Etudiant.java·Java
public class Etudiant extends Personne {
    // Attributs : filiere
    // Constructeur : Etudiant(String prenom, String filiere)
    // Méthode à écrire : public void sePresenter()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : ines

        // Appelez la méthode sePresenter() ici
    }
}
Voir la solution
Personne.java·Java
public class Personne {
    protected String prenom;

    public Personne(String prenom) {
        this.prenom = prenom;
    }

    public void sePresenter() {
        System.out.println("Je m'appelle " + prenom + ".");
    }
}
Etudiant.java·Java
public class Etudiant extends Personne {
    private String filiere;

    public Etudiant(String prenom, String filiere) {
        super(prenom);
        this.filiere = filiere;
    }

    @Override
    public void sePresenter() {
        super.sePresenter();                       // le code de la mère
        System.out.println("J'étudie : " + filiere + ".");   // + celui de la fille
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Etudiant ines = new Etudiant("Inès", "Informatique");
        ines.sePresenter();
    }
}
Console
Je m'appelle Inès.
J'étudie : Informatique.

super(...) appelle le constructeur de la mère ; super.methode() appelle une méthode de la mère. Sans cette ligne, la fille remplacerait entièrement le comportement de la mère au lieu de le compléter.

Ma confianceNon évalué
✓ Vérifiable

Exercice 19.4.A Trouver la faute

Cette classe ne compile pas. Pourquoi ? Corrigez-la.

Poule.java·Java
public class Poule extends Animal {

    public Poule(String nom, int age) {
        super(nom, age);
    }

    @Override
    public void crie() {
        System.out.println(nom + " fait : Cot-cot !");
    }
}
Voir la solution

La méthode s'appelle crie() alors que la méthode de la classe mère est crier(). Comme @Override est présent, le compilateur détecte qu'il n'y a rien à redéfinir et refuse de compiler. Il suffit de renommer la méthode en crier().

Ma confianceNon évalué
Non vérifiable

Exercice 19.5.A Des formes géométriques

Créez trois classes :

  1. Forme : une méthode aire() qui renvoie 0 ;
  2. Rectangle (largeur, longueur) : redéfinit aire() ;
  3. Cercle (rayon) : redéfinit aire().

Affichez l'aire d'un rectangle 4×5 (20.0) et d'un cercle de rayon 2.

Votre code à compléter

Forme.java·Java
public class Forme {
    // Méthode à écrire : public double aire()
}
Rectangle.java·Java
public class Rectangle extends Forme {
    // Attributs : largeur, longueur
    // Constructeur : Rectangle(double largeur, double longueur)
    // Méthode à écrire : public double aire()
}
Cercle.java·Java
public class Cercle extends Forme {
    // Attributs : rayon
    // Constructeur : Cercle(double rayon)
    // Méthode à écrire : public double aire()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : formes

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Forme.java·Java
public class Forme {
    public double aire() {
        return 0;
    }
}
Rectangle.java·Java
public class Rectangle extends Forme {
    private double largeur;
    private double longueur;

    public Rectangle(double largeur, double longueur) {
        this.largeur = largeur;
        this.longueur = longueur;
    }

    @Override
    public double aire() {
        return largeur * longueur;
    }
}
Cercle.java·Java
public class Cercle extends Forme {
    private double rayon;

    public Cercle(double rayon) {
        this.rayon = rayon;
    }

    @Override
    public double aire() {
        return Math.PI * rayon * rayon;
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Forme[] formes = { new Rectangle(4, 5), new Cercle(2) };

        for (Forme f : formes) {
            System.out.println("Aire : " + f.aire());
        }
    }
}
Console
Aire : 20.0
Aire : 12.566370614359172
Ma confianceNon évalué
✓ Vérifiable

Exercice 19.5.B Des instruments de musique

Créez trois classes :

  1. Instrument :
    • un attribut nom ;
    • une méthode jouer() qui affiche <nom> fait un son.
  2. Piano, fille d'Instrument, qui redéfinit jouer() pour afficher <nom> : do ré mi ! ;
  3. Guitare, fille d'Instrument, qui redéfinit jouer() pour afficher <nom> : ♪ strum strum ♪.

Rangez un Piano, une Guitare et un Instrument simple dans un tableau d'Instrument, puis faites-les tous jouer avec une seule boucle :

Console
Piano à queue : do ré mi !
Folk : ♪ strum strum ♪
Triangle fait un son.

Votre code à compléter

Instrument.java·Java
public class Instrument {
    // Attributs : nom
    // Constructeur : Instrument(String nom)
    // Méthode à écrire : public void jouer()
}
Piano.java·Java
public class Piano extends Instrument {
    // Constructeur : Piano(String nom)
    // Méthode à écrire : public void jouer()
}
Guitare.java·Java
public class Guitare extends Instrument {
    // Constructeur : Guitare(String nom)
    // Méthode à écrire : public void jouer()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Complétez ici

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Instrument.java·Java
public class Instrument {
    protected String nom;

    public Instrument(String nom) {
        this.nom = nom;
    }

    public void jouer() {
        System.out.println(nom + " fait un son.");
    }
}
Piano.java·Java
public class Piano extends Instrument {

    public Piano(String nom) {
        super(nom);
    }

    @Override
    public void jouer() {
        System.out.println(nom + " : do ré mi !");
    }
}
Guitare.java·Java
public class Guitare extends Instrument {

    public Guitare(String nom) {
        super(nom);
    }

    @Override
    public void jouer() {
        System.out.println(nom + " : ♪ strum strum ♪");
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Instrument[] orchestre = {
            new Piano("Piano à queue"),
            new Guitare("Folk"),
            new Instrument("Triangle")
        };

        for (Instrument instrument : orchestre) {
            instrument.jouer();    // chacun joue à sa façon
        }
    }
}
Console
Piano à queue : do ré mi !
Folk : ♪ strum strum ♪
Triangle fait un son.

Une seule boucle, trois comportements : c'est le polymorphisme. La variable instrument est de type Instrument, mais Java appelle la version de l'objet réel.

Ma confianceNon évalué
✓ Vérifiable

Exercice 19.5.C La facture polymorphe

Un magasin vend des Article (TVA à 20 %) et des Livre (TVA à 5,5 %).

  1. Article :
    • les attributs nom et prixHt ;
    • une méthode prixTtc() qui renvoie prixHt + prixHt * 0.2.
  2. Livre, qui hérite d'Article et redéfinit prixTtc() avec prixHt + prixHt * 0.055.

Dans main :

  • rangez un stylo à 10 €, un roman (livre) à 20 € et un sac à 50 € dans une ArrayList<Article> ;
  • affichez chaque ligne <nom> : <prixTtc> €, puis le total.

Résultat attendu :

Console
Stylo : 12.0 €
Roman : 21.1 €
Sac : 60.0 €
Total : 93.1 €

Votre code à compléter

Article.java·Java
public class Article {
    // Attributs : nom, prixHt
    // Constructeur : Article(String nom, double prixHt)
    // Méthode à écrire : public String getNom()
    // Méthode à écrire : public double prixTtc()
}
Livre.java·Java
public class Livre extends Article {
    // Constructeur : Livre(String nom, double prixHt)
    // Méthode à écrire : public double prixTtc()
}
Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : panier

        // Appelez la méthode add() ici

        // Déclarez votre variable ici : total

        // Parcourez avec une boucle for-each ici

        // Affichez le résultat ici
    }
}
Voir la solution
Article.java·Java
public class Article {
    protected String nom;
    protected double prixHt;

    public Article(String nom, double prixHt) {
        this.nom = nom;
        this.prixHt = prixHt;
    }

    public String getNom() {
        return nom;
    }

    public double prixTtc() {
        return prixHt + prixHt * 0.2;
    }
}
Livre.java·Java
public class Livre extends Article {

    public Livre(String nom, double prixHt) {
        super(nom, prixHt);
    }

    @Override
    public double prixTtc() {
        return prixHt + prixHt * 0.055;
    }
}
Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<Article> panier = new ArrayList<>();
        panier.add(new Article("Stylo", 10));
        panier.add(new Livre("Roman", 20));
        panier.add(new Article("Sac", 50));

        double total = 0;
        for (Article article : panier) {
            System.out.println(article.getNom() + " : " + article.prixTtc() + " €");
            total += article.prixTtc();
        }

        System.out.println("Total : " + total + " €");
    }
}
Console
Stylo : 12.0 €
Roman : 21.1 €
Sac : 60.0 €
Total : 93.1 €

Le code de la boucle ne change pas si on ajoute un Jouet ou un Disque avec sa propre TVA : c'est l'objet qui sait se calculer.

Ma confianceNon évalué
✓ Vérifiable

Exercice 19.5.D Qui répond ? Type de la variable ou type de l'objet

Voici une hiérarchie Animal → Chien → Chiot. Sans lancer le programme, écrivez ce qu'affichent les trois lignes de main. Puis expliquez pourquoi la ligne en commentaire ne compile pas.

Animal.java·Java
public class Animal {
    public void parler() {
        System.out.println("...");
    }
}
Chien.java·Java
public class Chien extends Animal {
    @Override
    public void parler() {
        System.out.println("Wouf");
    }

    public void garder() {
        System.out.println("Je garde la maison.");
    }
}
Chiot.java·Java
public class Chiot extends Chien {
    @Override
    public void parler() {
        System.out.println("Yip !");
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Animal a = new Chien();
        Animal b = new Chiot();
        Chien c = new Chiot();

        a.parler();
        b.parler();
        c.parler();

        // a.garder();      // ne compile pas : pourquoi ?
    }
}
Voir la solution
Console
Wouf
Yip !
Yip !

Java appelle toujours la version de l'objet réel (new …), pas celle du type de la variable : a contient un Chien, b et c contiennent un Chiot.

Mais le compilateur ne regarde que le type de la variable. a est un Animal, et Animal n'a pas de méthode garder() : la ligne ne compile pas, même si l'objet réel est un Chien. Pour l'appeler, il faut convertir (caster) : ((Chien) a).garder();.

Ma confianceNon évalué
Non vérifiable

Testez-vous : QCM

QCM · Chapitre 19

  1. Que se passe-t-il si on écrit @Override sur une méthode qui n'existe pas dans la classe mère ?

  2. Quel mot-clé exprime l'héritage ?

  3. À quoi sert super(nom, age) dans un constructeur de classe fille ?

  4. Que fait @Override ?

  5. Soit Animal a = new Chien("Rex", 4); a.crier(); — quelle version de crier() est appelée ?

Chapitre 20 · Héritage, classes abstraites et interfaces

Les classes abstraites

Objectif. Définir une classe « trop générale pour exister seule » qui impose à ses filles d'écrire certaines méthodes.

Glossaire du chapitre

Classe abstraite
Classe trop générale pour exister seule : on ne peut pas l'instancier avec new.
Méthode abstraite
Méthode sans corps, terminée par ;, que les classes filles doivent écrire.
Classe concrète
Classe normale, qui peut être instanciée.
Instanciation
Création d'un objet avec new.

4 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 20.2.A Un moyen de paiement

Créez :

  • une classe abstraite Paiement, avec une méthode abstraite payer(double montant) ;
  • deux classes filles :
    • CarteBancaire : affiche Paiement de X € par carte ;
    • Especes : affiche Paiement de X € en espèces.

Testez avec une boucle sur un tableau de Paiement.

Votre code à compléter

Paiement.java·Java
public abstract class Paiement {
    // Méthode abstraite à déclarer : public abstract void payer(double montant)
}
CarteBancaire.java·Java
public class CarteBancaire extends Paiement {
    // Méthode à écrire : public void payer(double montant)
}
Especes.java·Java
public class Especes extends Paiement {
    // Méthode à écrire : public void payer(double montant)
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : moyens

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Paiement.java·Java
public abstract class Paiement {
    public abstract void payer(double montant);
}
CarteBancaire.java·Java
public class CarteBancaire extends Paiement {
    @Override
    public void payer(double montant) {
        System.out.println("Paiement de " + montant + " € par carte");
    }
}
Especes.java·Java
public class Especes extends Paiement {
    @Override
    public void payer(double montant) {
        System.out.println("Paiement de " + montant + " € en espèces");
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Paiement[] moyens = { new CarteBancaire(), new Especes() };

        for (Paiement moyen : moyens) {
            moyen.payer(12.5);
        }
    }
}
Console
Paiement de 12.5 € par carte
Paiement de 12.5 € en espèces
Ma confianceNon évalué
✓ Vérifiable

Exercice 20.2.B Des notifications

Créez une classe abstraite Notification :

  • un attribut destinataire ;
  • une méthode abstraite envoyer(String message) ;
  • une méthode classique afficherEntete() qui affiche --- Pour <destinataire> ---.

Puis deux classes filles :

  • Email : envoyer affiche Email : <message> ;
  • Sms : envoyer affiche SMS : <message>.

Dans main, envoyez Votre colis arrive. à Léa par email et à Hugo par SMS, en appelant afficherEntete() avant chaque envoi :

Console
--- Pour Léa ---
Email : Votre colis arrive.
--- Pour Hugo ---
SMS : Votre colis arrive.

Votre code à compléter

Notification.java·Java
public abstract class Notification {
    // Attributs : destinataire
    // Constructeur : Notification(String destinataire)
    // Méthode abstraite à déclarer : public abstract void envoyer(String message)
    // Méthode à écrire : public void afficherEntete()
}
Email.java·Java
public class Email extends Notification {
    // Constructeur : Email(String destinataire)
    // Méthode à écrire : public void envoyer(String message)
}
Sms.java·Java
public class Sms extends Notification {
    // Constructeur : Sms(String destinataire)
    // Méthode à écrire : public void envoyer(String message)
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : notifications

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Notification.java·Java
public abstract class Notification {
    protected String destinataire;

    public Notification(String destinataire) {
        this.destinataire = destinataire;
    }

    public abstract void envoyer(String message);

    public void afficherEntete() {
        System.out.println("--- Pour " + destinataire + " ---");
    }
}
Email.java·Java
public class Email extends Notification {

    public Email(String destinataire) {
        super(destinataire);
    }

    @Override
    public void envoyer(String message) {
        System.out.println("Email : " + message);
    }
}
Sms.java·Java
public class Sms extends Notification {

    public Sms(String destinataire) {
        super(destinataire);
    }

    @Override
    public void envoyer(String message) {
        System.out.println("SMS : " + message);
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Notification[] notifications = { new Email("Léa"), new Sms("Hugo") };

        for (Notification n : notifications) {
            n.afficherEntete();                   // méthode classique, héritée telle quelle
            n.envoyer("Votre colis arrive.");     // méthode abstraite, écrite par chaque fille
        }
    }
}
Console
--- Pour Léa ---
Email : Votre colis arrive.
--- Pour Hugo ---
SMS : Votre colis arrive.
Ma confianceNon évalué
✓ Vérifiable

Exercice 20.2.C Pourquoi ça ne compile pas ?

Voici une classe abstraite et deux classes qui la mal utilisent. Pour chaque message du compilateur, expliquez la cause, puis corrigez le code.

Boisson.java·Java
public abstract class Boisson {
    protected String nom;

    public Boisson(String nom) {
        this.nom = nom;
    }

    public abstract double prix();
}
Cafe.java·Java
// ne compile pas
public class Cafe extends Boisson {

    public Cafe() {
        super("Café");
    }
}
Console
Cafe.java:1: error: Cafe is not abstract and does not override abstract method prix() in Boisson
public class Cafe extends Boisson {
       ^
Main.java·Java
// ne compile pas
public class Main {
    public static void main(String[] args) {
        Boisson b = new Boisson("Eau");
    }
}
Console
Main.java:3: error: Boisson is abstract; cannot be instantiated
        Boisson b = new Boisson("Eau");
                    ^
Voir la solution
  • Première erreur : Cafe hérite de Boisson mais n'écrit pas la méthode abstraite prix(). Une classe concrète doit écrire toutes les méthodes abstraites de sa mère. Correction : ajouter prix() dans Cafe (ou rendre Cafe abstraite, ce qui n'aurait pas de sens ici).
  • Seconde erreur : on ne peut pas faire new sur une classe abstraite. Correction : créer un objet d'une fille (new Cafe()), qu'on peut ranger dans une variable de type Boisson.
Cafe.java·Java
public class Cafe extends Boisson {

    public Cafe() {
        super("Café");
    }

    @Override
    public double prix() {
        return 1.8;
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Boisson b = new Cafe();
        System.out.println(b.nom + " : " + b.prix() + " €");
    }
}
Console
Café : 1.8 €
Ma confianceNon évalué
✓ Vérifiable

Exercice 20.2.D Les salaires d'une entreprise

Créez une classe abstraite Employe :

  • un attribut nom ;
  • une méthode abstraite double salaire() ;
  • une méthode classique afficher() qui écrit <nom> : <salaire> €.

Puis deux classes filles :

  • Salarie : un salaire fixe donné au constructeur ;
  • Freelance : un nombre de jours travaillés et un tarif par jour ; salaire() renvoie jours * tarif.

Affichez un salarié à 2800 € et un freelance qui a travaillé 12 jours à 250 €/jour :

Console
Nora : 2800.0 €
Ilyès : 3000.0 €

Votre code à compléter

Employe.java·Java
public abstract class Employe {
    // Attributs : nom
    // Constructeur : Employe(String nom)
    // Méthode abstraite à déclarer : public abstract double salaire()
    // Méthode à écrire : public void afficher()
}
Salarie.java·Java
public class Salarie extends Employe {
    // Attributs : salaireFixe
    // Constructeur : Salarie(String nom, double salaireFixe)
    // Méthode à écrire : public double salaire()
}
Freelance.java·Java
public class Freelance extends Employe {
    // Attributs : jours, tarifParJour
    // Constructeur : Freelance(String nom, int jours, double tarifParJour)
    // Méthode à écrire : public double salaire()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : equipe

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Employe.java·Java
public abstract class Employe {
    protected String nom;

    public Employe(String nom) {
        this.nom = nom;
    }

    public abstract double salaire();

    public void afficher() {
        System.out.println(nom + " : " + salaire() + " €");
    }
}
Salarie.java·Java
public class Salarie extends Employe {
    private double salaireFixe;

    public Salarie(String nom, double salaireFixe) {
        super(nom);
        this.salaireFixe = salaireFixe;
    }

    @Override
    public double salaire() {
        return salaireFixe;
    }
}
Freelance.java·Java
public class Freelance extends Employe {
    private int jours;
    private double tarifParJour;

    public Freelance(String nom, int jours, double tarifParJour) {
        super(nom);
        this.jours = jours;
        this.tarifParJour = tarifParJour;
    }

    @Override
    public double salaire() {
        return jours * tarifParJour;
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Employe[] equipe = { new Salarie("Nora", 2800), new Freelance("Ilyès", 12, 250) };

        for (Employe e : equipe) {
            e.afficher();
        }
    }
}
Console
Nora : 2800.0 €
Ilyès : 3000.0 €

afficher() est écrite une seule fois dans la classe abstraite, et appelle salaire() : Java choisit la bonne version selon l'objet. C'est l'intérêt d'une classe abstraite : du code partagé et des méthodes imposées.

Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 20

  1. Peut-on écrire new Vehicule("x") si Vehicule est abstraite ?

  2. Une méthode abstraite…

  3. Que doit faire une classe fille concrète d'une classe abstraite ?

  4. Une classe abstraite peut-elle contenir des méthodes classiques ?

Chapitre 21 · Héritage, classes abstraites et interfaces

Interfaces et Comparable

Objectif. Comprendre ce qu'est une interface (un contrat), l'utiliser pour donner des capacités à des classes, puis s'en servir pour trier des objets avec Comparable.

Glossaire du chapitre

Interface
Contrat qui liste des méthodes à écrire. Une classe peut en implémenter plusieurs.
implements
Une classe s'engage à respecter une interface.
Interface native
Interface fournie avec Java (Comparable, List, Comparator…).
Comparable
Interface native (java.lang) qui permet à un objet de se comparer pour être trié.
compareTo
Méthode de Comparable : renvoie un nombre négatif (avant), 0 (égal) ou positif (après).
Comparator
« Juge » extérieur qui compare deux objets, pour trier autrement.
toString()
Méthode qui décide du texte affiché quand on écrit System.out.println(objet).

10 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 21.2.A Un Nageur de plus

Créez une classe Humain qui implémente Nageur et affiche Un humain nage la brasse.. Ajoutez-en un au tableau du bassin.

Votre code à compléter

Humain.java·Java
public class Humain implements Nageur {
    // Méthode à écrire : public void nager()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : bassin

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Humain.java·Java
public class Humain implements Nageur {

    @Override
    public void nager() {
        System.out.println("Un humain nage la brasse.");
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Nageur[] bassin = { new Canard("Donald"), new Poisson(), new Humain() };

        for (Nageur n : bassin) {
            n.nager();
        }
    }
}
Console
Donald nage sur l'étang.
Le poisson nage sous l'eau.
Un humain nage la brasse.
Ma confianceNon évalué
Non vérifiable

Exercice 21.2.B Deux contrats pour un seul objet

Créez :

  • deux interfaces : Volant (méthode voler()) et Plongeur (méthode plonger()) ;
  • Canard, qui implémente les deux ;
  • Avion, qui n'implémente que Volant.

Dans main :

  1. rangez un Canard et un Avion dans une ArrayList<Volant> et faites-les voler ;
  2. faites plonger un Canard rangé dans une variable de type Plongeur.
Console
Le canard bat des ailes.
L'avion décolle.
Le canard plonge dans l'étang.

Votre code à compléter

Volant.java·Java
public interface Volant {
    // Méthode abstraite à déclarer : void voler()
}
Plongeur.java·Java
public interface Plongeur {
    // Méthode abstraite à déclarer : void plonger()
}
Canard.java·Java
public class Canard implements Volant, Plongeur {
    // Méthode à écrire : public void voler()
    // Méthode à écrire : public void plonger()
}
Avion.java·Java
public class Avion implements Volant {
    // Méthode à écrire : public void voler()
}
Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : volants

        // Appelez la méthode add() ici

        // Parcourez avec une boucle for-each ici

        // Créez votre objet ici : nageur

        // Appelez la méthode plonger() ici
    }
}
Voir la solution
Volant.java·Java
public interface Volant {
    void voler();
}
Plongeur.java·Java
public interface Plongeur {
    void plonger();
}
Canard.java·Java
public class Canard implements Volant, Plongeur {

    @Override
    public void voler() {
        System.out.println("Le canard bat des ailes.");
    }

    @Override
    public void plonger() {
        System.out.println("Le canard plonge dans l'étang.");
    }
}
Avion.java·Java
public class Avion implements Volant {

    @Override
    public void voler() {
        System.out.println("L'avion décolle.");
    }
}
Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<Volant> volants = new ArrayList<>();
        volants.add(new Canard());
        volants.add(new Avion());

        for (Volant v : volants) {
            v.voler();
        }

        Plongeur nageur = new Canard();
        nageur.plonger();
    }
}
Console
Le canard bat des ailes.
L'avion décolle.
Le canard plonge dans l'étang.

Une classe n'hérite que d'une seule classe, mais elle peut implémenter autant d'interfaces qu'on veut : une interface est un rôle, et un objet peut jouer plusieurs rôles.

Ma confianceNon évalué
✓ Vérifiable

Exercice 21.2.C Pourquoi ça ne compile pas ? (une interface)

Cette classe implémente l'interface Payable, mais le compilateur refuse. Lisez le message, expliquez-le et corrigez.

Payable.java·Java
public interface Payable {
    double montant();
}
Facture.java·Java
// ne compile pas
public class Facture implements Payable {

    @Override
    double montant() {
        return 120.0;
    }
}
Console
Facture.java:4: error: montant() in Facture cannot implement montant() in Payable
    double montant() {
           ^
  attempting to assign weaker access privileges; was public
Voir la solution

Dans une interface, toutes les méthodes sont publiques automatiquement (même sans écrire public). En les implémentant, on ne peut pas réduire leur visibilité : la version de Facture, sans modificateur (visibilité « package »), est moins visible que public, d'où « weaker access privileges ». Il suffit d'ajouter public :

Facture.java·Java
public class Facture implements Payable {

    @Override
    public double montant() {
        return 120.0;
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Payable p = new Facture();
        System.out.println("À payer : " + p.montant() + " €");
    }
}
Console
À payer : 120.0 €
Ma confianceNon évalué
✓ Vérifiable

Exercice 21.3.A Trier des prénoms

Triez la liste Noé, Inès, Léa, Hugo par ordre alphabétique avec Collections.sort et affichez-la.

Votre code à compléter

Main.java·Java
import java.util.ArrayList;
import java.util.Collections;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : prenoms

        // Appelez la méthode add() ici

        // Appelez la méthode sort() ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.util.ArrayList;
import java.util.Collections;

public class Main {
    public static void main(String[] args) {
        ArrayList<String> prenoms = new ArrayList<>();
        prenoms.add("Noé");
        prenoms.add("Inès");
        prenoms.add("Léa");
        prenoms.add("Hugo");

        Collections.sort(prenoms);

        System.out.println(prenoms);   // [Hugo, Inès, Léa, Noé]
    }
}
Console
[Hugo, Inès, Léa, Noé]
Ma confianceNon évalué
✓ Vérifiable

Exercice 21.3.B Trier des livres par nombre de pages

Créez une classe Livre (titre, pages) qui implémente Comparable<Livre> pour trier par nombre de pages croissant. Triez trois livres et affichez-les.

Votre code à compléter

Livre.java·Java
public class Livre implements Comparable<Livre> {
    // Attributs : titre, pages
    // Constructeur : Livre(String titre, int pages)
    // Méthode à écrire : public int compareTo(Livre autre)
    // Méthode à écrire : public String toString()
}
Main.java·Java
import java.util.ArrayList;
import java.util.Collections;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : livres

        // Appelez la méthode add() ici

        // Appelez la méthode sort() ici

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Livre.java·Java
public class Livre implements Comparable<Livre> {
    private String titre;
    private int pages;

    public Livre(String titre, int pages) {
        this.titre = titre;
        this.pages = pages;
    }

    @Override
    public int compareTo(Livre autre) {
        return Integer.compare(this.pages, autre.pages);
    }

    @Override
    public String toString() {
        return titre + " (" + pages + " pages)";
    }
}
Main.java·Java
import java.util.ArrayList;
import java.util.Collections;

public class Main {
    public static void main(String[] args) {
        ArrayList<Livre> livres = new ArrayList<>();
        livres.add(new Livre("Le Petit Prince", 96));
        livres.add(new Livre("Les Misérables", 1500));
        livres.add(new Livre("Candide", 180));

        Collections.sort(livres);

        for (Livre l : livres) {
            System.out.println(l);
        }
    }
}
Console
Le Petit Prince (96 pages)
Candide (180 pages)
Les Misérables (1500 pages)
Ma confianceNon évalué
✓ Vérifiable

Exercice 21.3.C Trier des élèves par nom

Créez une classe Eleve (attribut nom) qui implémente Comparable<Eleve> pour trier par ordre alphabétique des noms. Triez Sacha, Camille, Yanis et affichez la liste.

Votre code à compléter

Eleve.java·Java
public class Eleve implements Comparable<Eleve> {
    // Attributs : nom
    // Constructeur : Eleve(String nom)
    // Méthode à écrire : public int compareTo(Eleve autre)
    // Méthode à écrire : public String toString()
}
Main.java·Java
import java.util.ArrayList;
import java.util.Collections;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : eleves

        // Appelez la méthode add() ici

        // Appelez la méthode sort() ici

        // Affichez le résultat ici
    }
}
Voir la solution
Eleve.java·Java
public class Eleve implements Comparable<Eleve> {
    private String nom;

    public Eleve(String nom) {
        this.nom = nom;
    }

    @Override
    public int compareTo(Eleve autre) {
        return this.nom.compareTo(autre.nom);
    }

    @Override
    public String toString() {
        return nom;
    }
}
Main.java·Java
import java.util.ArrayList;
import java.util.Collections;

public class Main {
    public static void main(String[] args) {
        ArrayList<Eleve> eleves = new ArrayList<>();
        eleves.add(new Eleve("Sacha"));
        eleves.add(new Eleve("Camille"));
        eleves.add(new Eleve("Yanis"));

        Collections.sort(eleves);

        System.out.println(eleves);
    }
}
Console
[Camille, Sacha, Yanis]
Ma confianceNon évalué
✓ Vérifiable

Exercice 21.3.D Du plus haut niveau au plus bas

Rappel : la classe Joueur du cours a deux attributs, pseudo et niveau, et son compareTo trie par niveau croissant (section 21.3).

Modifiez Joueur.compareTo pour trier du niveau le plus élevé au plus bas.

Voir la solution
Java
@Override
public int compareTo(Joueur autre) {
    return Integer.compare(autre.niveau, this.niveau);   // inversé
}
Ma confianceNon évalué
Non vérifiable

Exercice 21.4.A La liste de courses avec List

Dans cet exercice, déclarez votre liste avec le type List<String> à gauche du = (et non ArrayList).

  1. Créez une liste courses contenant pain, lait et œufs.
  2. Ajoutez beurre, puis retirez lait.
  3. Écrivez une méthode static void afficher(List<String> liste) qui affiche chaque élément précédé de -, puis une dernière ligne Total : N article(s).
  4. Appelez cette méthode depuis main.

Déroulé attendu :

Console
- pain
- œufs
- beurre
Total : 3 article(s)

Votre code à compléter

Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    // Méthode à écrire : static void afficher(List<String> liste)

    public static void main(String[] args) {
        // Créez votre liste ici : courses

        // Appelez la méthode add() ici

        // Appelez la méthode remove() ici

        // Complétez ici
    }
}
Voir la solution

La variable est déclarée avec List<String> et fabriquée avec new ArrayList<>(). La méthode afficher prend une List<String> : elle fonctionnerait avec n'importe quelle sorte de liste.

Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {

    static void afficher(List<String> liste) {
        for (String article : liste) {
            System.out.println("- " + article);
        }
        System.out.println("Total : " + liste.size() + " article(s)");
    }

    public static void main(String[] args) {
        List<String> courses = new ArrayList<>();
        courses.add("pain");
        courses.add("lait");
        courses.add("œufs");

        courses.add("beurre");
        courses.remove("lait");

        afficher(courses);
    }
}
Ma confianceNon évalué
✓ Vérifiable

Exercice 21.4.B Trier avec un Comparator

Un Livre a un titre et un nombrePages.

  1. Ajoutez une méthode toString() à Livre.
  2. Écrivez deux « juges » : ParTitre (ordre alphabétique des titres) et ParPagesDecroissant (du plus épais au plus mince).
  3. Triez la même liste de trois livres de deux façons et affichez-la à chaque fois.

Indice : pour inverser un tri sur des nombres, comparez b à a au lieu de a à b.

Console
[Alice (160 p.), Dune (612 p.), Zadig (96 p.)]
[Dune (612 p.), Alice (160 p.), Zadig (96 p.)]

Votre code à compléter

Livre.java·Java
public class Livre {
    // Attributs : titre, nombrePages
    // Constructeur : Livre(String titre, int nombrePages)
    // Méthode à écrire : public String getTitre()
    // Méthode à écrire : public int getNombrePages()
    // Méthode à écrire : public String toString()
}
ParTitre.java·Java
import java.util.Comparator;

public class ParTitre implements Comparator<Livre> {
    // Méthode à écrire : public int compare(Livre a, Livre b)
}
ParPagesDecroissant.java·Java
import java.util.Comparator;

public class ParPagesDecroissant implements Comparator<Livre> {
    // Méthode à écrire : public int compare(Livre a, Livre b)
}
Main.java·Java
import java.util.ArrayList;
import java.util.Collections;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : livres

        // Appelez la méthode add() ici

        // Appelez la méthode sort() ici

        // Affichez le résultat ici

        // Appelez la méthode sort() ici

        // Affichez le résultat ici
    }
}
Voir la solution
Livre.java·Java
public class Livre {
    private String titre;
    private int nombrePages;

    public Livre(String titre, int nombrePages) {
        this.titre = titre;
        this.nombrePages = nombrePages;
    }

    public String getTitre() {
        return titre;
    }

    public int getNombrePages() {
        return nombrePages;
    }

    @Override
    public String toString() {
        return titre + " (" + nombrePages + " p.)";
    }
}
ParTitre.java·Java
import java.util.Comparator;

public class ParTitre implements Comparator<Livre> {

    @Override
    public int compare(Livre a, Livre b) {
        return a.getTitre().compareTo(b.getTitre());
    }
}
ParPagesDecroissant.java·Java
import java.util.Comparator;

public class ParPagesDecroissant implements Comparator<Livre> {

    @Override
    public int compare(Livre a, Livre b) {
        return Integer.compare(b.getNombrePages(), a.getNombrePages());   // b avant a : ordre inversé
    }
}
Main.java·Java
import java.util.ArrayList;
import java.util.Collections;

public class Main {
    public static void main(String[] args) {
        ArrayList<Livre> livres = new ArrayList<>();
        livres.add(new Livre("Dune", 612));
        livres.add(new Livre("Alice", 160));
        livres.add(new Livre("Zadig", 96));

        Collections.sort(livres, new ParTitre());
        System.out.println(livres);

        Collections.sort(livres, new ParPagesDecroissant());
        System.out.println(livres);
    }
}
Console
[Alice (160 p.), Dune (612 p.), Zadig (96 p.)]
[Dune (612 p.), Alice (160 p.), Zadig (96 p.)]
Ma confianceNon évalué
✓ Vérifiable

Exercice 21.4.C Une liste de tâches à exécuter (Runnable)

Vous préparez le programme de la matinée sous forme d'actions.

  1. Écrivez une classe Cafe qui implémente Runnable : son run() affiche Préparer le café.
  2. Écrivez une classe Mail qui implémente Runnable : son run() affiche Lire les mails.
  3. Dans main, créez une liste List<Runnable> matin contenant un Cafe, un Mail, puis un deuxième Cafe.
  4. Parcourez la liste et déclenchez chaque action avec run().

Déroulé attendu :

Console
Préparer le café
Lire les mails
Préparer le café

Votre code à compléter

Cafe.java·Java
public class Cafe implements Runnable {
    // Méthode à écrire : public void run()
}
Mail.java·Java
public class Mail implements Runnable {
    // Méthode à écrire : public void run()
}
Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : matin

        // Appelez la méthode add() ici

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution

Les deux classes n'ont rien en commun, sauf Runnable. C'est ce qui permet de les ranger dans la même liste et de les déclencher de la même façon, sans savoir ce que chacune fait.

Cafe.java·Java
public class Cafe implements Runnable {

    @Override
    public void run() {
        System.out.println("Préparer le café");
    }
}
Mail.java·Java
public class Mail implements Runnable {

    @Override
    public void run() {
        System.out.println("Lire les mails");
    }
}
Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Runnable> matin = new ArrayList<>();
        matin.add(new Cafe());
        matin.add(new Mail());
        matin.add(new Cafe());

        for (Runnable tache : matin) {
            tache.run();
        }
    }
}
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 21

  1. Quel mot-clé utilise une classe pour respecter une interface ?

  2. Une classe peut-elle implémenter plusieurs interfaces ?

  3. Que doit renvoyer a.compareTo(b) si a doit passer avant b ?

  4. Pourquoi implémenter Comparable dans une classe ?

  5. D'où vient l'interface Comparable ?

  6. Quelle interface permet de trier les mêmes objets selon un autre critère, sans modifier leur classe ?

  7. Quel est l'avantage d'écrire List<String> noms = new ArrayList<>(); plutôt que ArrayList<String> noms = new ArrayList<>(); ?

  8. Combien d'ordres de tri différents peut-on définir pour la classe Joueur avec des Comparator ?

1 gros exercice ou projet

Exercice 21.4.D Gros exercice : le catalogue triable de la boutique

Exercice de synthèse du chapitre : on réunit interface, Comparable et Comparator sur un cas réaliste, le catalogue d'une boutique. Il n'utilise que ce qui a été vu dans ce chapitre. Les prix sont des nombres entiers (en euros).

1. Le contrat. Écrivez une interface Vendable avec deux méthodes : String getNom() et int getPrix().

2. Deux produits. Écrivez deux classes qui implémentent Vendable : Livre (nom, prix, auteur) et Jouet (nom, prix, âge minimum). Elles n'ont rien en commun à part le contrat Vendable. Ajoutez un toString() qui renvoie nom (prix €).

3. L'ordre naturel. Faites en sorte que Livre soit Comparable<Livre> : son ordre naturel est le prix croissant.

4. Deux arbitres. Écrivez deux Comparator<Vendable>, valables pour tous les produits : ParPrix (prix croissant) et ParNom (ordre alphabétique des noms).

5. Le catalogue. Dans main :

  • créez une List<Vendable> contenant deux livres et deux jouets, et affichez-la triée par prix puis par nom ;
  • créez une List<Livre> avec les deux livres et triez-la avec l'ordre naturel (Collections.sort(liste)) ;
  • affichez enfin le prix total du catalogue.

Déroulé attendu :

Console
Par prix : [Cube magique (12 €), Algo pour tous (20 €), Java facile (29 €), Robot wifi (45 €)]
Par nom : [Algo pour tous (20 €), Cube magique (12 €), Java facile (29 €), Robot wifi (45 €)]
Livres (ordre naturel) : [Algo pour tous (20 €), Java facile (29 €)]
Prix total : 106 €

Votre code à compléter

Vendable.java·Java
public interface Vendable {
    // Méthode abstraite à déclarer : String getNom()
    // Méthode abstraite à déclarer : int getPrix()
}
Livre.java·Java
public class Livre implements Vendable, Comparable<Livre> {
    // Attributs : nom, prix, auteur
    // Constructeur : Livre(String nom, int prix, String auteur)
    // Méthode à écrire : public String getNom()
    // Méthode à écrire : public int getPrix()
    // Méthode à écrire : public int compareTo(Livre autre)
    // Méthode à écrire : public String toString()
}
Jouet.java·Java
public class Jouet implements Vendable {
    // Attributs : nom, prix, ageMin
    // Constructeur : Jouet(String nom, int prix, int ageMin)
    // Méthode à écrire : public String getNom()
    // Méthode à écrire : public int getPrix()
    // Méthode à écrire : public String toString()
}
ParPrix.java·Java
import java.util.Comparator;

public class ParPrix implements Comparator<Vendable> {
    // Méthode à écrire : public int compare(Vendable a, Vendable b)
}
ParNom.java·Java
import java.util.Comparator;

public class ParNom implements Comparator<Vendable> {
    // Méthode à écrire : public int compare(Vendable a, Vendable b)
}
Main.java·Java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : java

        // Créez votre objet ici : algo

        // Créez votre liste ici : catalogue

        // Appelez la méthode add() ici

        // Appelez la méthode sort() ici

        // Affichez le résultat ici

        // Appelez la méthode sort() ici

        // Affichez le résultat ici

        // Créez votre liste ici : livres

        // Appelez la méthode add() ici

        // Appelez la méthode sort() ici

        // Affichez le résultat ici

        // Déclarez votre variable ici : total

        // Parcourez avec une boucle for-each ici

        // Affichez le résultat ici
    }
}
Voir la solution

Le point clé : la liste est de type List<Vendable> (l'interface), donc on peut y mélanger Livre et Jouet et les trier avec un Comparator<Vendable> qui n'utilise que les méthodes du contrat (getPrix(), getNom()). Seul Livre a un ordre naturel, grâce à Comparable.

Vendable.java·Java
public interface Vendable {
    String getNom();
    int getPrix();
}
Livre.java·Java
public class Livre implements Vendable, Comparable<Livre> {
    private String nom;
    private int prix;
    private String auteur;

    public Livre(String nom, int prix, String auteur) {
        this.nom = nom;
        this.prix = prix;
        this.auteur = auteur;
    }

    @Override
    public String getNom() {
        return nom;
    }

    @Override
    public int getPrix() {
        return prix;
    }

    @Override
    public int compareTo(Livre autre) {
        return Integer.compare(this.prix, autre.prix);
    }

    @Override
    public String toString() {
        return nom + " (" + prix + " €)";
    }
}
Jouet.java·Java
public class Jouet implements Vendable {
    private String nom;
    private int prix;
    private int ageMin;

    public Jouet(String nom, int prix, int ageMin) {
        this.nom = nom;
        this.prix = prix;
        this.ageMin = ageMin;
    }

    @Override
    public String getNom() {
        return nom;
    }

    @Override
    public int getPrix() {
        return prix;
    }

    @Override
    public String toString() {
        return nom + " (" + prix + " €)";
    }
}
ParPrix.java·Java
import java.util.Comparator;

public class ParPrix implements Comparator<Vendable> {

    @Override
    public int compare(Vendable a, Vendable b) {
        return Integer.compare(a.getPrix(), b.getPrix());
    }
}
ParNom.java·Java
import java.util.Comparator;

public class ParNom implements Comparator<Vendable> {

    @Override
    public int compare(Vendable a, Vendable b) {
        return a.getNom().compareTo(b.getNom());
    }
}
Main.java·Java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Livre java = new Livre("Java facile", 29, "K. Martin");
        Livre algo = new Livre("Algo pour tous", 20, "A. Petit");

        List<Vendable> catalogue = new ArrayList<>();
        catalogue.add(java);
        catalogue.add(algo);
        catalogue.add(new Jouet("Cube magique", 12, 6));
        catalogue.add(new Jouet("Robot wifi", 45, 10));

        Collections.sort(catalogue, new ParPrix());
        System.out.println("Par prix : " + catalogue);

        Collections.sort(catalogue, new ParNom());
        System.out.println("Par nom : " + catalogue);

        List<Livre> livres = new ArrayList<>();
        livres.add(java);
        livres.add(algo);
        Collections.sort(livres);
        System.out.println("Livres (ordre naturel) : " + livres);

        int total = 0;
        for (Vendable v : catalogue) {
            total += v.getPrix();
        }
        System.out.println("Prix total : " + total + " €");
    }
}
Ma confianceNon évalué
✓ Vérifiable

Partie 8 · 8 / 14

Les erreurs

Chapitre 22

Chapitre 22 · Les erreurs

Les exceptions et le débogueur

Objectif. Comprendre pourquoi un programme plante, lire le message d'erreur, rattraper une exception ou en lever une, et trouver un bug avec le débogueur d'IntelliJ.

Glossaire du chapitre

Exception
Événement anormal pendant l'exécution (division par zéro, indice inexistant…) qui interrompt le programme s'il n'est pas géré.
Trace d'appels (stack trace)
Message affiché lors d'un plantage : le nom de l'exception, son message, puis les lignes at … qui indiquent où cela s'est passé. La première ligne at est la plus utile.
try / catch
try entoure le code risqué ; catch rattrape l'exception et exécute une alternative au lieu de laisser le programme planter.
throw
Lève une exception soi-même, par exemple pour refuser une valeur invalide (throw new IllegalArgumentException("…")).
throws
Déclare qu'une méthode peut lever une exception vérifiée, que l'appelant devra traiter.
Exception non vérifiée (unchecked)
Hérite de RuntimeException : le compilateur ne force pas à la traiter. Elle signale souvent un bug à corriger ou une donnée invalide.
Exception vérifiée (checked)
Hérite de Exception sans passer par RuntimeException : le compilateur oblige à la rattraper (try/catch) ou à la déclarer (throws).
Exception personnalisée
Sa propre classe d'exception, créée avec extends Exception (ou RuntimeException) pour un cas métier précis.
try-with-resources
Écriture try (Scanner s = …) { … } qui ferme automatiquement la ressource à la fin du bloc.
Débogueur
Outil de l'IDE qui exécute le programme pas à pas pour observer la valeur des variables et trouver un bug.
Point d'arrêt (breakpoint)
Ligne où le débogueur met le programme en pause.
Step Over / Step Into / Step Out
Exécuter la ligne sans entrer dans les méthodes / entrer dans la méthode appelée / terminer la méthode en cours.

15 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 22.1.A Lire une trace

Lancez ce programme. Quelle exception est levée ? À quelle ligne ? Pourquoi ? Corrigez-le pour qu'il affiche 10, 20 et 30.

Main.java·Java
public class Main {
    public static void main(String[] args) {
        int[] scores = {10, 20, 30};

        for (int i = 1; i <= scores.length; i++) {
            System.out.println(scores[i]);
        }
    }
}
Voir la solution

La console affiche :

Console
20
30
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
	at Main.main(Main.java:6)

La première ligne at désigne la ligne 6. Avant de planter, le programme a affiché 20 et 30 : la boucle commence à l'indice 1 au lieu de 0, et va jusqu'à 3 à cause du <=. Deux corrections :

Main.java·Java
public class Main {
    public static void main(String[] args) {
        int[] scores = {10, 20, 30};

        for (int i = 0; i < scores.length; i++) {
            System.out.println(scores[i]);
        }
    }
}
Console
10
20
30
Ma confianceNon évalué
✓ Vérifiable

Exercice 22.2.A Compilation ou exécution ? Analyser une erreur

Pour chacun des six programmes (le code donné est le corps du main), dites si l'erreur arrive à la compilation (le programme ne démarre même pas) ou à l'exécution (le programme démarre, puis plante), puis où il faut regarder pour la corriger.

  • A : int age = "vingt";
  • B : String s = "Java"; System.out.println(s.size());
  • C : int[] t = {1, 2, 3}; System.out.println(t[3]);
  • D : System.out.println("Bonjour")
  • E : int x; System.out.println(x);
  • F : String s = null; if (s.equals("a")) { System.out.println("oui"); }
Voir la solution
Cas Quand ? Message À corriger
A compilation incompatible types: String cannot be converted to int on range un texte dans un int : changer le type ou la valeur
B compilation cannot find symbol … symbol: method size() location: variable s of type String String n'a pas de méthode size() : c'est length()
C exécution ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3 l'indice 3 n'existe pas : 0, 1 ou 2
D compilation ';' expected il manque le ; en fin de ligne
E compilation variable x might not have been initialized x n'a jamais reçu de valeur : en donner une
F exécution NullPointerException: Cannot invoke "String.equals(Object)" because "s" is null s vaut null : tester "a".equals(s) ou vérifier s != null

Comment les reconnaître ? Une erreur de compilation est soulignée en rouge par IntelliJ avant même de lancer le programme, et la console affiche error:. Une exception d'exécution n'est visible qu'après avoir lancé, avec Exception in thread "main" suivi d'une trace.

Le plus utile : lire le message en entier, avec le type d'erreur, et la ligne indiquée. Dans une trace, la première ligne at qui vient de votre code est celle à regarder en premier.

Ma confianceNon évalué
Non vérifiable

Exercice 22.2.B Analyser une trace : le produit qui n'existe pas

Ce programme plante au deuxième appel. Lisez la trace, puis répondez :

  1. quelle exception ?
  2. quelle ligne de votre code est en cause ?
  3. pourquoi ?
  4. comment corriger pour afficher 0 quand le produit est inconnu ?
Main.java·Java
import java.util.HashMap;
import java.util.Map;

public class Main {

    static int prixDuProduit(Map<String, Integer> catalogue, String produit) {
        return catalogue.get(produit);
    }

    public static void main(String[] args) {
        Map<String, Integer> catalogue = new HashMap<>();
        catalogue.put("Baguette", 1);
        catalogue.put("Croissant", 2);

        System.out.println(prixDuProduit(catalogue, "Baguette"));
        System.out.println(prixDuProduit(catalogue, "Éclair"));
    }
}
Console
1
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.get(Object)" is null
    at Main.prixDuProduit(Main.java:7)
    at Main.main(Main.java:16)
Voir la solution
  1. Une NullPointerException. Le message dit : Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.get(Object)" is null.
  2. La première ligne at qui vient de notre code désigne Main.prixDuProduit, ligne 7 : return catalogue.get(produit);. L'appel vient de main, ligne 17.
  3. "Éclair" n'est pas dans le catalogue : get renvoie null. Or la méthode promet de renvoyer un int, et un int ne peut pas être null : Java tente de convertir le null en int (méthode intValue()) et lève la NullPointerException. (Voir la section 27.4.)
  4. On utilise getOrDefault, qui renvoie une valeur par défaut quand la clé est absente :
Main.java·Java
import java.util.HashMap;
import java.util.Map;

public class Main {

    static int prixDuProduit(Map<String, Integer> catalogue, String produit) {
        return catalogue.getOrDefault(produit, 0);
    }

    public static void main(String[] args) {
        Map<String, Integer> catalogue = new HashMap<>();
        catalogue.put("Baguette", 1);
        catalogue.put("Croissant", 2);

        System.out.println(prixDuProduit(catalogue, "Baguette"));
        System.out.println(prixDuProduit(catalogue, "Éclair"));
    }
}
Console
1
0
Ma confianceNon évalué
✓ Vérifiable

Exercice 22.4.A Une saisie qui ne plante pas

Lisez un entier et affichez son double : Le double de 7 est 14. Si l'utilisateur tape autre chose qu'un nombre, affichez Ce n'est pas un nombre. au lieu de planter.

Votre code à compléter

Main.java·Java
import java.util.InputMismatchException;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Écrivez votre try / catch ici
    }
}

⌨ Valeur à saisir

Voir la solution
Main.java·Java
import java.util.InputMismatchException;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        try {
            int nombre = scanner.nextInt();
            System.out.println("Le double de " + nombre + " est " + (nombre * 2));
        } catch (InputMismatchException e) {
            System.out.println("Ce n'est pas un nombre.");
        }
    }
}

⌨ Valeur à saisir

Console
7
Le double de 7 est 14
Ma confianceNon évalué
✓ Vérifiable

Exercice 22.4.B Redemander jusqu'à obtenir un nombre

Demandez un âge tant que la saisie n'est pas un nombre, puis affichez-le :

Console
Votre âge : vingt
Ce n'est pas un nombre, recommencez.
Votre âge : 25
Âge enregistré : 25

Indice : une boucle while avec un boolean, et souvenez-vous du piège du buffer (6.6).

Votre code à compléter

Main.java·Java
import java.util.InputMismatchException;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Déclarez votre variable ici : age

        // Déclarez votre variable ici : valide

        // Écrivez votre boucle ici (while)

        // Affichez le résultat ici
    }
}

⌨ Valeurs à saisir, dans cet ordre

Voir la solution
Main.java·Java
import java.util.InputMismatchException;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int age = 0;
        boolean valide = false;

        while (!valide) {
            System.out.print("Votre âge : ");
            try {
                age = scanner.nextInt();
                valide = true;
            } catch (InputMismatchException e) {
                System.out.println("Ce n'est pas un nombre, recommencez.");
                scanner.nextLine();   // jette la saisie invalide, restée dans le buffer
            }
        }

        System.out.println("Âge enregistré : " + age);
    }
}

⌨ Valeurs à saisir, dans cet ordre

Console
Votre âge : vingt
Ce n'est pas un nombre, recommencez.
Votre âge : 25
Âge enregistré : 25

Sans le scanner.nextLine() du catch, la saisie « vingt » reste dans le buffer : nextInt() la relit à chaque tour, et la boucle ne s'arrête jamais (le bouton rouge Stop l'interrompt).

Ma confianceNon évalué
✓ Vérifiable

Exercice 22.4.C Quelle exception ?

Pour chaque morceau de code ci-dessous (placé dans un main), dites quelle exception est levée à l'exécution. Écrivez votre réponse avant de lancer quoi que ce soit.

  • A : int[] t = new int[5]; t[5] = 1;
  • B : String s = null; System.out.println(s.trim());
  • C : int n = Integer.parseInt("12,5");
  • D : int x = 7 / (3 - 3);
  • E : Object o = 42; String s = (String) o;
  • F : ArrayList<String> liste = new ArrayList<>(); liste.get(0);

Vérifiez ensuite en écrivant un seul programme qui exécute les six cas, chacun dans son propre try / catch (Exception e), et affiche e.getClass().getSimpleName() (le nom de la classe de l'exception).

Votre code à compléter

Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // Écrivez votre try / catch ici
    }
}
Voir la solution
Cas Exception Pourquoi
A ArrayIndexOutOfBoundsException un tableau de 5 cases a les indices 0 à 4
B NullPointerException s ne désigne aucun objet, on ne peut pas appeler trim()
C NumberFormatException « 12,5 » n'est pas un entier (et la virgule n'est pas un point)
D ArithmeticException 3 - 3 vaut 0 : division entière par zéro
E ClassCastException un Integer n'est pas un String
F IndexOutOfBoundsException la liste est vide : il n'y a pas d'indice 0
Main.java·Java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        try {
            int[] t = new int[5];
            t[5] = 1;
        } catch (Exception e) {
            System.out.println("A : " + e.getClass().getSimpleName());
        }

        try {
            String s = null;
            System.out.println(s.trim());
        } catch (Exception e) {
            System.out.println("B : " + e.getClass().getSimpleName());
        }

        try {
            int n = Integer.parseInt("12,5");
        } catch (Exception e) {
            System.out.println("C : " + e.getClass().getSimpleName());
        }

        try {
            int x = 7 / (3 - 3);
        } catch (Exception e) {
            System.out.println("D : " + e.getClass().getSimpleName());
        }

        try {
            Object o = 42;
            String s = (String) o;
        } catch (Exception e) {
            System.out.println("E : " + e.getClass().getSimpleName());
        }

        try {
            ArrayList<String> liste = new ArrayList<>();
            liste.get(0);
        } catch (Exception e) {
            System.out.println("F : " + e.getClass().getSimpleName());
        }
    }
}
Console
A : ArrayIndexOutOfBoundsException
B : NullPointerException
C : NumberFormatException
D : ArithmeticException
E : ClassCastException
F : IndexOutOfBoundsException

catch (Exception e) rattrape ici toutes les exceptions : pratique pour un test comme celui-ci, mais dans un vrai programme on préfère le type le plus précis possible.

Ma confianceNon évalué
✓ Vérifiable

Exercice 22.4.D Deux erreurs possibles, deux messages

Un programme demande le nombre de bonbons, puis le nombre d'enfants. Il affiche combien chaque enfant en reçoit et combien il en reste (division entière et modulo %).

Gérez deux erreurs avec deux catch :

  • une saisie qui n'est pas un nombre : Saisie invalide : il faut des nombres entiers. ;
  • un nombre d'enfants égal à 0 : Impossible de partager entre 0 enfant.

Dans tous les cas, un bloc finally affiche Merci !.

Testez avec 17 et 5, puis 17 et 0, puis beaucoup.

Votre code à compléter

Main.java·Java
import java.util.InputMismatchException;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // Créez le Scanner ici

        // Écrivez votre try / catch ici
    }
}

⌨ Valeurs à saisir, dans cet ordre

Voir la solution
Main.java·Java
import java.util.InputMismatchException;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        try {
            System.out.print("Bonbons : ");
            int bonbons = scanner.nextInt();
            System.out.print("Enfants : ");
            int enfants = scanner.nextInt();

            System.out.println("Chacun reçoit " + (bonbons / enfants) + " bonbons.");
            System.out.println("Il en reste " + (bonbons % enfants) + ".");
        } catch (InputMismatchException e) {
            System.out.println("Saisie invalide : il faut des nombres entiers.");
        } catch (ArithmeticException e) {
            System.out.println("Impossible de partager entre 0 enfant.");
        } finally {
            System.out.println("Merci !");
        }
    }
}

⌨ Valeurs à saisir, dans cet ordre

Console
Bonbons : 17
Enfants : 5
Chacun reçoit 3 bonbons.
Il en reste 2.
Merci !

⌨ Valeurs à saisir, dans cet ordre

Console
Bonbons : 17
Enfants : 0
Impossible de partager entre 0 enfant.
Merci !

⌨ Valeur à saisir

Console
Bonbons : beaucoup
Saisie invalide : il faut des nombres entiers.
Merci !

Le modulo % par zéro lève aussi une ArithmeticException : une seule catch suffit pour les deux opérations.

Ma confianceNon évalué
✓ Vérifiable

Exercice 22.4.E Convertir des textes en nombres

Une liste de saisies arrive sous forme de textes : "12", "abc", "-5", "30", "7,5".

  1. Écrivez static int convertir(String texte) qui renvoie l'entier correspondant, mais qui lève une IllegalArgumentException (avec le message nombre négatif : <texte>) si le nombre est négatif.
  2. Dans main, parcourez les cinq textes et affichez :
    • "<texte>" → <nombre> si tout va bien ;
    • "<texte>" → pas un nombre pour une NumberFormatException ;
    • "<texte>" → refusé (<message>) pour une IllegalArgumentException.
  3. Affichez enfin combien de saisies étaient valides.

Attention à l'ordre de vos deux catch : NumberFormatException est un cas particulier d'IllegalArgumentException.

Votre code à compléter

Main.java·Java
public class Main {
    // Méthode à écrire : static int convertir(String texte)

    public static void main(String[] args) {
        // Créez votre tableau ici : saisies

        // Déclarez votre variable ici : valides

        // Parcourez avec une boucle for-each ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
public class Main {

    static int convertir(String texte) {
        int nombre = Integer.parseInt(texte);       // peut lever NumberFormatException
        if (nombre < 0) {
            throw new IllegalArgumentException("nombre négatif : " + texte);
        }
        return nombre;
    }

    public static void main(String[] args) {
        String[] saisies = {"12", "abc", "-5", "30", "7,5"};
        int valides = 0;

        for (String texte : saisies) {
            try {
                int nombre = convertir(texte);
                System.out.println("\"" + texte + "\" → " + nombre);
                valides++;
            } catch (NumberFormatException e) {          // le plus précis d'abord
                System.out.println("\"" + texte + "\" → pas un nombre");
            } catch (IllegalArgumentException e) {       // le plus général ensuite
                System.out.println("\"" + texte + "\" → refusé (" + e.getMessage() + ")");
            }
        }

        System.out.println(valides + " saisies valides sur " + saisies.length);
    }
}
Console
"12" → 12
"abc" → pas un nombre
"-5" → refusé (nombre négatif : -5)
"30" → 30
"7,5" → pas un nombre
2 saisies valides sur 5

Si on inverse les deux catch, le compilateur refuse : exception NumberFormatException has already been caught. Le catch (IllegalArgumentException e) placé en premier rattraperait déjà tout ce qui hérite d'IllegalArgumentException, NumberFormatException comprise.

Ma confianceNon évalué
✓ Vérifiable

Exercice 22.5.A Le finally : prédire la sortie

Lisez ce programme sans le lancer et écrivez ce qu'il affiche. Vérifiez ensuite avec IntelliJ.

Main.java·Java
public class Main {

    static void diviser(int a, int b) {
        System.out.println("Début de " + a + " / " + b);
        try {
            System.out.println("Résultat : " + (a / b));
        } catch (ArithmeticException e) {
            System.out.println("Erreur : division par zéro");
        } finally {
            System.out.println("Fin de l'opération");
        }
        System.out.println("Après le bloc");
    }

    public static void main(String[] args) {
        diviser(10, 2);
        diviser(10, 0);
    }
}

Question bonus : que se passe-t-il si on supprime le bloc catch (en gardant le finally) ? Le finally s'exécute-t-il quand même pour diviser(10, 0) ? « Après le bloc » est-il affiché ?

Voir la solution
Console
Début de 10 / 2
Résultat : 5
Fin de l'opération
Après le bloc
Début de 10 / 0
Erreur : division par zéro
Fin de l'opération
Après le bloc

Sans le catch, l'exception n'est plus rattrapée. Pour diviser(10, 0), le finally s'exécute quand même (« Fin de l'opération » est affiché), mais « Après le bloc » ne l'est pas : l'exception continue de remonter après le finally, et le programme s'arrête.

Main.java·Java
public class Main {

    static void diviser(int a, int b) {
        System.out.println("Début de " + a + " / " + b);
        try {
            System.out.println("Résultat : " + (a / b));
        } finally {
            System.out.println("Fin de l'opération");
        }
        System.out.println("Après le bloc");
    }

    public static void main(String[] args) {
        diviser(10, 2);
        diviser(10, 0);
    }
}
Console
Début de 10 / 2
Résultat : 5
Fin de l'opération
Après le bloc
Début de 10 / 0
Fin de l'opération
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Main.diviser(Main.java:6)
    at Main.main(Main.java:15)
Ma confianceNon évalué
Non vérifiable

Exercice 22.6.A Une carte cadeau qui refuse

Reprenez la CarteCadeau de l'exercice 15.3.A.

  • Au lieu d'afficher « Solde insuffisant. », depenser lève une IllegalArgumentException avec un message.
  • Dans main, dépensez 20 € puis 40 € sur une carte de 50 €, rattrapez l'exception et affichez son message :
Console
Refusé : Solde insuffisant : 30.0 € restants
Solde : 30.0 €

Votre code à compléter

CarteCadeau.java·Java
public class CarteCadeau {
    // Attributs : solde
    // Constructeur : CarteCadeau(double solde)
    // Méthode à écrire : public double getSolde()
    // Méthode à écrire : public void depenser(double montant)
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : carte

        // Appelez la méthode depenser() ici

        // Écrivez votre try / catch ici

        // Affichez le résultat ici
    }
}
Voir la solution
CarteCadeau.java·Java
public class CarteCadeau {
    private double solde;

    public CarteCadeau(double solde) {
        this.solde = solde;
    }

    public double getSolde() {
        return solde;
    }

    public void depenser(double montant) {
        if (montant > solde) {
            throw new IllegalArgumentException("Solde insuffisant : " + solde + " € restants");
        }
        solde -= montant;
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        CarteCadeau carte = new CarteCadeau(50);

        carte.depenser(20);
        try {
            carte.depenser(40);
        } catch (IllegalArgumentException e) {
            System.out.println("Refusé : " + e.getMessage());
        }

        System.out.println("Solde : " + carte.getSolde() + " €");
    }
}
Console
Refusé : Solde insuffisant : 30.0 € restants
Solde : 30.0 €
Ma confianceNon évalué
✓ Vérifiable

Exercice 22.8.A Notre exception vérifiée

Créez une exception vérifiée AgeInvalideException, puis écrivez static void inscrire(String prenom, int age) :

  • elle lève l'exception si age est inférieur à 18 ;
  • sinon, elle affiche par exemple Hugo est inscrit.

Dans main, inscrivez Hugo (20 ans) puis Léa (16 ans) :

Console
Hugo est inscrit.
Inscription refusée. Léa a 16 ans : il faut avoir 18 ans.

Votre code à compléter

AgeInvalideException.java·Java
public class AgeInvalideException extends Exception {
    // Constructeur : AgeInvalideException(String message)
}
Main.java·Java
public class Main {
    // Méthode à écrire : static void inscrire(String prenom, int age) throws AgeInvalideException

    public static void main(String[] args) {
        // Écrivez votre try / catch ici
    }
}
Voir la solution
AgeInvalideException.java·Java
public class AgeInvalideException extends Exception {

    public AgeInvalideException(String message) {
        super(message);
    }
}
Main.java·Java
public class Main {

    static void inscrire(String prenom, int age) throws AgeInvalideException {
        if (age < 18) {
            throw new AgeInvalideException(prenom + " a " + age + " ans : il faut avoir 18 ans.");
        }
        System.out.println(prenom + " est inscrit.");
    }

    public static void main(String[] args) {
        try {
            inscrire("Hugo", 20);
            inscrire("Léa", 16);
        } catch (AgeInvalideException e) {
            System.out.println("Inscription refusée. " + e.getMessage());
        }
    }
}
Console
Hugo est inscrit.
Inscription refusée. Léa a 16 ans : il faut avoir 18 ans.

Sans le throws AgeInvalideException, le code ne compile pas : l'exception est vérifiée.

Ma confianceNon évalué
✓ Vérifiable

Exercice 22.8.B Le distributeur de billets

Créez une exception vérifiée FondsInsuffisantsException :

  • elle garde le montant manquant (double manque, avec un getter).

Créez la classe Distributeur :

  • un attribut solde ;
  • retirer(double montant) lève cette exception (avec throws) si montant dépasse le solde.

Dans main :

  • un distributeur de 200 € ; retirez 80 €, puis 150 € ;
  • rattrapez l'exception pour afficher Refusé : il manque 30.0 € ;
  • un bloc finally affiche Carte rendue. à la fin.

Votre code à compléter

FondsInsuffisantsException.java·Java
public class FondsInsuffisantsException extends Exception {
    // Attributs : manque
    // Constructeur : FondsInsuffisantsException(double manque)
    // Méthode à écrire : public double getManque()
}
Distributeur.java·Java
public class Distributeur {
    // Attributs : solde
    // Constructeur : Distributeur(double solde)
    // Méthode à écrire : public void retirer(double montant) throws FondsInsuffisantsException
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : distributeur

        // Écrivez votre try / catch ici
    }
}
Voir la solution
FondsInsuffisantsException.java·Java
public class FondsInsuffisantsException extends Exception {
    private double manque;

    public FondsInsuffisantsException(double manque) {
        super("Fonds insuffisants");
        this.manque = manque;
    }

    public double getManque() {
        return manque;
    }
}
Distributeur.java·Java
public class Distributeur {
    private double solde;

    public Distributeur(double solde) {
        this.solde = solde;
    }

    public void retirer(double montant) throws FondsInsuffisantsException {
        if (montant > solde) {
            throw new FondsInsuffisantsException(montant - solde);
        }
        solde -= montant;
        System.out.println("Retrait de " + montant + " € : il reste " + solde + " €");
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Distributeur distributeur = new Distributeur(200);

        try {
            distributeur.retirer(80);
            distributeur.retirer(150);
            System.out.println("Jamais affiché");
        } catch (FondsInsuffisantsException e) {
            System.out.println("Refusé : il manque " + e.getManque() + " €");
        } finally {
            System.out.println("Carte rendue.");
        }
    }
}
Console
Retrait de 80.0 € : il reste 120.0 €
Refusé : il manque 30.0 €
Carte rendue.

Une exception est une classe comme une autre : elle peut avoir ses propres attributs et ses getters, pour transmettre à l'appelant plus qu'un message.

Ma confianceNon évalué
✓ Vérifiable

Exercice 22.9.A Suivre la propagation

Voici trois méthodes qui s'appellent en chaîne. Sans lancer le programme, répondez :

  1. qu'affiche le programme, ligne par ligne ?
  2. quelles lignes ne s'affichent jamais ?
  3. qui rattrape l'exception ?
Main.java·Java
public class Main {

    static void a() {
        System.out.println("a : début");
        b();
        System.out.println("a : fin");
    }

    static void b() {
        System.out.println("b : début");
        try {
            c();
        } catch (ArithmeticException e) {
            System.out.println("b a rattrapé : " + e.getMessage());
        }
        System.out.println("b : fin");
    }

    static void c() {
        System.out.println("c : début");
        int x = 5 / 0;
        System.out.println("c : fin " + x);
    }

    public static void main(String[] args) {
        a();
        System.out.println("main : fin");
    }
}

Question bonus : si le catch de b rattrapait un NullPointerException au lieu d'un ArithmeticException, que se passerait-il ?

Voir la solution
Console
a : début
b : début
c : début
b a rattrapé : / by zero
b : fin
a : fin
main : fin

c : fin ne s'affiche jamais (l'exception interrompt c). C'est b qui rattrape l'exception : à partir de là, tout redevient normal pour a et pour main, qui ne savent même pas qu'il y a eu un problème.

Avec catch (NullPointerException e), le catch de b ne correspond pas : l'exception traverse b, puis a, puis main, et le programme s'arrête avec une trace. Les lignes « b : fin », « a : fin » et « main : fin » ne s'affichent pas :

Main.java·Java
public class Main {

    static void a() {
        System.out.println("a : début");
        b();
        System.out.println("a : fin");
    }

    static void b() {
        System.out.println("b : début");
        try {
            c();
        } catch (NullPointerException e) {
            System.out.println("b a rattrapé : " + e.getMessage());
        }
        System.out.println("b : fin");
    }

    static void c() {
        System.out.println("c : début");
        int x = 5 / 0;
        System.out.println("c : fin " + x);
    }

    public static void main(String[] args) {
        a();
        System.out.println("main : fin");
    }
}
Console
a : début
b : début
c : début
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Main.c(Main.java:21)
    at Main.b(Main.java:12)
    at Main.a(Main.java:5)
    at Main.main(Main.java:26)
Ma confianceNon évalué
Non vérifiable

Exercice 22.12.A Chasse aux bugs au débogueur

Ce programme compile et ne plante pas, mais il affiche un résultat faux :

Console
Articles : 4
Total : 4.0 €

Où poser les points d'arrêt et quel parcours suivre :

  1. Cliquez dans la marge (à gauche des numéros de ligne) pour poser un point d'arrêt sur deux lignes : la ligne 6 (nombre += quantites[i];) et la ligne 14 (total = prix[i] * quantites[i];). Deux pastilles rouges apparaissent.
  2. Lancez avec Debug (l'insecte, ou Maj+F9). Le programme s'arrête sur la ligne 6, avant de l'exécuter.
  3. Bug 1. Dans le panneau Variables, lisez i : il vaut 1 alors que ce premier tour devrait avoir i = 0, et nombre vaut 0. La case d'indice 0 (la quantité 2) ne sera jamais ajoutée. Regardez la ligne for : le départ de la boucle est faux.
  4. Appuyez sur Resume (F9) : l'exécution repart et s'arrête au tour suivant (i = 2, nombre = 3). Encore F9 : la boucle est finie, le programme s'arrête sur la ligne 14.
  5. Bug 2. Sur la ligne 14, i = 0 et total = 0.0. Faites un Step Over (F8) : total vaut maintenant 5.0 (2.4 × 2). F9 pour le tour suivant : i = 1 et total = 5.0. Nouveau F8 : total vaut 4.5 (1.5 × 3) au lieu de 9.5. Le total a diminué : la valeur précédente a été écrasée au lieu d'être ajoutée.
  6. Arrêtez le débogueur (carré rouge), corrigez les deux lignes, relancez normalement : vous devez lire 6 articles et 14.5 €.
Main.java·Java
public class Main {

    static int nombreArticles(int[] quantites) {
        int nombre = 0;
        for (int i = 1; i < quantites.length; i++) {
            nombre += quantites[i];
        }
        return nombre;
    }

    static double total(double[] prix, int[] quantites) {
        double total = 0;
        for (int i = 0; i < prix.length; i++) {
            total = prix[i] * quantites[i];
        }
        return total;
    }

    public static void main(String[] args) {
        double[] prix = {2.5, 1.5, 4.0};
        int[] quantites = {2, 3, 1};

        System.out.println("Articles : " + nombreArticles(quantites));
        System.out.println("Total : " + total(prix, quantites) + " €");
    }
}
Voir la solution
  • nombreArticles : la boucle commence à i = 1, la première quantité (2) est oubliée. Le débogueur montre i = 1 dès le premier tour.
  • total : total = … remplace la valeur à chaque tour au lieu de l'ajouter. Au dernier tour, total vaut 4.0 × 1.
Main.java·Java
public class Main {

    static int nombreArticles(int[] quantites) {
        int nombre = 0;
        for (int i = 0; i < quantites.length; i++) {
            nombre += quantites[i];
        }
        return nombre;
    }

    static double total(double[] prix, int[] quantites) {
        double total = 0;
        for (int i = 0; i < prix.length; i++) {
            total += prix[i] * quantites[i];
        }
        return total;
    }

    public static void main(String[] args) {
        double[] prix = {2.5, 1.5, 4.0};
        int[] quantites = {2, 3, 1};

        System.out.println("Articles : " + nombreArticles(quantites));
        System.out.println("Total : " + total(prix, quantites) + " €");
    }
}
Console
Articles : 6
Total : 13.5 €
Ma confianceNon évalué
✓ Vérifiable

Exercice 22.13.A Débogueur : entrer dans une méthode

Ce programme doit offrir les frais de port à partir de 50 € d'achats. Or il affiche À payer : 54.9 € pour un panier de 50,00 €. Trouvez le bug avec le débogueur, sans ajouter de println.

Main.java·Java
public class Main {

    static double sousTotal(double[] prix) {
        double total = 0;
        for (double p : prix) {
            total += p;
        }
        return total;
    }

    static double fraisDePort(double sousTotal) {
        if (sousTotal > 50) {
            return 0;
        }
        return 4.9;
    }

    public static void main(String[] args) {
        double[] prix = {20.0, 15.5, 14.5};
        double st = sousTotal(prix);
        double port = fraisDePort(st);
        System.out.println("Sous-total : " + st + " €");
        System.out.println("Port : " + port + " €");
        System.out.println("À payer : " + (st + port) + " €");
    }
}

Le parcours à suivre :

  1. Posez un seul point d'arrêt, ligne 21 : double port = fraisDePort(st);. Lancez avec l'insecte.
  2. Dans le panneau Variables, vérifiez st : vaut-il bien 50.0 ?
  3. Step Into (F7) pour entrer dans fraisDePort. Que vaut sousTotal ?
  4. Avec Evaluate Expression (Alt + F8), calculez sousTotal > 50. Quelle valeur ?
  5. Step Over (F8) : sur quelle ligne le débogueur s'arrête-t-il ? Qu'en concluez-vous ? Corrigez.
Voir la solution
  1. L'arrêt a lieu sur la ligne 21, avant son exécution : st = 50.0, le sous-total est correct (20 + 15,5 + 14,5). Le bug n'est donc pas dans sousTotal.
  2. Après Step Into, le débogueur est ligne 12 : sousTotal = 50.0.
  3. sousTotal > 50 vaut false : 50.0 n'est pas strictement supérieur à 50.
  4. Step Over passe de la ligne 12 à la ligne 15 (return 4.9;) : le bloc return 0; est sauté. La condition > exclut 50 pile, alors qu'on voulait « à partir de 50 ».
  5. Correction : if (sousTotal >= 50).
Main.java·Java
public class Main {

    static double sousTotal(double[] prix) {
        double total = 0;
        for (double p : prix) {
            total += p;
        }
        return total;
    }

    static double fraisDePort(double sousTotal) {
        if (sousTotal >= 50) {
            return 0;
        }
        return 4.9;
    }

    public static void main(String[] args) {
        double[] prix = {20.0, 15.5, 14.5};
        double st = sousTotal(prix);
        double port = fraisDePort(st);
        System.out.println("Sous-total : " + st + " €");
        System.out.println("Port : " + port + " €");
        System.out.println("À payer : " + (st + port) + " €");
    }
}
Console
Sous-total : 50.0 €
Port : 0.0 €
À payer : 50.0 €

Ce type de bug, un > à la place d'un >=, est l'un des plus fréquents : le programme tourne sans erreur, et seul un cas limite (ici, exactement 50) révèle le problème. Pensez à tester les valeurs aux bornes.

Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 22

  1. Que se passe-t-il si une exception n'est rattrapée nulle part ?

  2. Dans une trace d'appels, par où commencer ?

  3. Quel mot-clé lève une exception ?

  4. Une exception vérifiée…

  5. Que vaut 10.0 / 0 ?

  6. Votre point d'arrêt est posé, mais le programme ne s'arrête pas. Pourquoi ?

  7. Quel bloc s'exécute dans tous les cas, qu'une exception arrive ou non ?

  8. main appelle a(), qui appelle b(), qui appelle c(). c() lève une exception que personne ne rattrape. Que se passe-t-il ?

  9. Quelle est la différence entre throw et throws ?

  10. Pourquoi 10 / 0 lève une exception alors que 10.0 / 0 donne Infinity ?

  11. Un même try est suivi de catch (IllegalArgumentException e) et de catch (NumberFormatException e). Dans quel ordre faut-il les écrire ?

  12. Dans le débogueur, quelle action permet d'entrer dans la méthode appelée sur la ligne courante ?

  13. Quelle erreur est détectée avant même de lancer le programme ?

1 gros exercice ou projet

Exercice 22.13.B Gros exercice : l'importateur de commandes robuste

Exercice de synthèse du chapitre. On traite une liste de commandes dont certaines données sont fausses : une ligne fautive ne doit jamais faire planter tout l'import. On combine try/catch, plusieurs types d'exceptions, finally, une exception maison et throw.

On dispose de trois tableaux, rangés dans le même ordre : le nom du produit, la quantité commandée (saisie sous forme de texte) et le prix unitaire en euros. Le tableau des quantités est volontairement trop court :

Java
String[] produits = {"Baguette", "Croissant", "Pain", "Jus", "Café"};
String[] quantites = {"3", "deux", "-2", "5"};
int[] prix = {2, 3, 2, 4, 3};

Pour chaque produit (même indice dans les trois tableaux), à gérer dans un try par tour de boucle :

  • quantité correcte → calculer quantité × prix et l'ajouter au total général ;
  • quantité qui n'est pas un nombre (Integer.parseInt échoue) → NumberFormatException : afficher « ignoré (nombre invalide) » et continuer ;
  • quantité négative ou nulle → lever votre exception QuantiteInvalideException : afficher « ignoré (quantité invalide) » et continuer ;
  • quantité absente (indice hors du tableau) → ArrayIndexOutOfBoundsException : afficher « ignoré (quantité manquante) » et continuer ;
  • dans un bloc finally, comptez les lignes traitées (réussies ou non).

À la fin, afficher le nombre de lignes traitées, le nombre de lignes acceptées et le total.

Déroulé attendu :

Console
Baguette : 6 €
Croissant -> ignoré (nombre invalide)
Pain -> ignoré (quantité invalide)
Jus : 20 €
Café -> ignoré (quantité manquante)
-----
5 ligne(s) traitée(s), 2 acceptée(s), total : 26 €
Voir la solution

Le cœur de l'exercice : un seul try par tour de boucle, avec plusieurs catch (un par type d'erreur). Dès qu'une ligne plante, son catch affiche un message et la boucle passe au produit suivant : le traitement n'est jamais interrompu. Le finally s'exécute à chaque tour, qu'il y ait eu une erreur ou non.

QuantiteInvalideException.java·Java
public class QuantiteInvalideException extends Exception {
    public QuantiteInvalideException(String message) {
        super(message);
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        String[] produits = {"Baguette", "Croissant", "Pain", "Jus", "Café"};
        String[] quantites = {"3", "deux", "-2", "5"};
        int[] prix = {2, 3, 2, 4, 3};

        int traitees = 0;
        int acceptees = 0;
        int total = 0;

        for (int i = 0; i < produits.length; i++) {
            try {
                int quantite = Integer.parseInt(quantites[i]);

                if (quantite <= 0) {
                    throw new QuantiteInvalideException("quantité = " + quantite);
                }

                int sousTotal = quantite * prix[i];
                total += sousTotal;
                acceptees++;
                System.out.println(produits[i] + " : " + sousTotal + " €");

            } catch (NumberFormatException e) {
                System.out.println(produits[i] + " -> ignoré (nombre invalide)");
            } catch (QuantiteInvalideException e) {
                System.out.println(produits[i] + " -> ignoré (quantité invalide)");
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println(produits[i] + " -> ignoré (quantité manquante)");
            } finally {
                traitees++;
            }
        }

        System.out.println("-----");
        System.out.println(traitees + " ligne(s) traitée(s), " + acceptees + " acceptée(s), total : " + total + " €");
    }
}
Ma confianceNon évalué
Non vérifiable

Partie 9 · 9 / 14

Les objets en pratique

Chapitre 23

Chapitre 23 · Les objets en pratique

toString, equals, hashCode et record

Objectif. Donner à vos objets un affichage lisible et une égalité sur leur contenu, pour qu'ils fonctionnent dans un HashSet ou comme clés d'une HashMap ; puis écrire tout cela en une ligne avec record.

Glossaire du chapitre

toString()
Méthode qui renvoie le texte affiché quand on imprime un objet. Sans elle, Java affiche un code illisible.
equals()
Méthode qui définit quand deux objets sont considérés comme égaux (même contenu).
hashCode()
Méthode qui renvoie un nombre résumant l'objet, utilisé par HashSet et HashMap pour le ranger. Elle doit toujours accompagner equals.
Contrat equals / hashCode
Deux objets égaux selon equals doivent avoir le même hashCode.
record
Classe compacte, immuable, qui génère toute seule constructeur, accesseurs, toString, equals et hashCode.
Constructeur compact
Constructeur d'un record sans paramètres écrits, utile pour valider les valeurs reçues.
Immuable
Dont l'état ne peut plus changer après sa création.
Génération IntelliJ
Menu Code › Generate… : IntelliJ écrit pour vous toString, equals et hashCode.

5 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 23.1.A Un panier lisible

Écrivez une classe Produit avec un nom et une quantité, et redéfinissez toString pour qu'une liste de produits s'affiche Panier : [Baguette x2, Croissant x6].

Votre code à compléter

Produit.java·Java
public class Produit {
    // Attributs : nom, quantite
    // Constructeur : Produit(String nom, int quantite)
    // Méthode à écrire : public String toString()
}
Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : panier

        // Appelez la méthode add() ici

        // Affichez le résultat ici
    }
}
Voir la solution
Produit.java·Java
public class Produit {
    private String nom;
    private int quantite;

    public Produit(String nom, int quantite) {
        this.nom = nom;
        this.quantite = quantite;
    }

    @Override
    public String toString() {
        return nom + " x" + quantite;
    }
}
Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Produit> panier = new ArrayList<>();
        panier.add(new Produit("Baguette", 2));
        panier.add(new Produit("Croissant", 6));
        System.out.println("Panier : " + panier);
    }
}
Console
Panier : [Baguette x2, Croissant x6]
Ma confianceNon évalué
✓ Vérifiable

Exercice 23.3.A Un client par carte

Une classe Client a un numéro de carte et un nom. Deux clients sont le même client s'ils ont le même numéro, même si leur nom est écrit différemment.

  1. Écrivez equals et hashCode.
  2. Ajoutez trois clients à un HashSet, dont deux avec le numéro C-001.

Le programme doit afficher Clients inscrits : 2.

Votre code à compléter

Client.java·Java
public class Client {
    // Attributs : numero, nom
    // Constructeur : Client(String numero, String nom)
    // Méthode à écrire : public boolean equals(Object autre)
    // Méthode à écrire : public int hashCode()
}
Main.java·Java
import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        // Créez votre ensemble (HashSet) ici : inscrits

        // Appelez la méthode add() ici

        // Affichez le résultat ici
    }
}
Voir la solution

equals et hashCode n'utilisent que le numéro : c'est lui qui fait l'identité du client.

Client.java·Java
public class Client {
    private String numero;
    private String nom;

    public Client(String numero, String nom) {
        this.numero = numero;
        this.nom = nom;
    }

    // Deux clients sont égaux s'ils ont le même numéro de carte
    @Override
    public boolean equals(Object autre) {
        if (this == autre) {
            return true;
        }
        if (!(autre instanceof Client client)) {
            return false;
        }
        return numero.equals(client.numero);
    }

    @Override
    public int hashCode() {
        return numero.hashCode();
    }
}
Main.java·Java
import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Set<Client> inscrits = new HashSet<>();
        inscrits.add(new Client("C-001", "Léa Martin"));
        inscrits.add(new Client("C-002", "Hugo Bernard"));
        inscrits.add(new Client("C-001", "Léa MARTIN"));

        System.out.println("Clients inscrits : " + inscrits.size());
    }
}
Console
Clients inscrits : 2

Un seul attribut : numero.hashCode() suffit, Objects.hash(numero) marcherait aussi.

Ma confianceNon évalué
✓ Vérifiable

Exercice 23.3.B Les places du théâtre

Une place se repère par sa rangée (une lettre) et son numéro.

  1. Créez une classe Place, avec toString, equals et hashCode.
  2. Créez une HashMap<Place, String> des réservations (place → nom du spectateur).
  3. Vérifiez qu'une place créée à part, new Place('B', 12), retrouve bien sa réservation, et que la place A1 est libre.

Votre code à compléter

Place.java·Java
import java.util.Objects;

public class Place {
    // Attributs : rangee, numero
    // Constructeur : Place(char rangee, int numero)
    // Méthode à écrire : public String toString()
    // Méthode à écrire : public boolean equals(Object autre)
    // Méthode à écrire : public int hashCode()
}
Main.java·Java
import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        // Créez votre dictionnaire (HashMap) ici : reservations

        // Appelez la méthode put() ici

        // Créez votre objet ici : demandee

        // Écrivez votre condition ici (if)

        // Affichez le résultat ici
    }
}
Voir la solution

equals et hashCode utilisent les mêmes attributs (rangée et numéro) : c'est ce qui permet à la HashMap de retrouver une place créée à part. (Section 23.5 : un record écrit tout cela à votre place.)

Place.java·Java
import java.util.Objects;

public class Place {
    private char rangee;
    private int numero;

    public Place(char rangee, int numero) {
        this.rangee = rangee;
        this.numero = numero;
    }

    @Override
    public String toString() {
        return "" + rangee + numero;
    }

    // Deux places sont égales si elles ont la même rangée et le même numéro
    @Override
    public boolean equals(Object autre) {
        if (this == autre) {
            return true;
        }
        if (!(autre instanceof Place place)) {
            return false;
        }
        return rangee == place.rangee && numero == place.numero;
    }

    // Même règle que equals : on utilise les mêmes attributs
    @Override
    public int hashCode() {
        return Objects.hash(rangee, numero);
    }
}
Main.java·Java
import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        HashMap<Place, String> reservations = new HashMap<>();
        reservations.put(new Place('B', 12), "Léa");
        reservations.put(new Place('C', 3), "Hugo");

        Place demandee = new Place('B', 12);
        if (reservations.containsKey(demandee)) {
            System.out.println("Place " + demandee + " réservée par " + reservations.get(demandee));
        }
        System.out.println("Place A1 libre : " + !reservations.containsKey(new Place('A', 1)));
    }
}
Console
Place B12 réservée par Léa
Place A1 libre : true
Ma confianceNon évalué
✓ Vérifiable

Exercice 23.5.A Une note valide

Écrivez un record Note :

  • deux composants : la matière et la valeur (décimale) ;
  • une valeur hors de 0 à 20 est refusée avec une IllegalArgumentException ;
  • une méthode estMoyenne() dit si la note vaut au moins 10.

Dans main, créez une note de 14,5, puis essayez une note de 25 et rattrapez l'exception.

Votre code à compléter

Note.java·Java
public record Note(String matiere, double valeur) {
    // Méthode à écrire : public boolean estMoyenne()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : maths

        // Affichez le résultat ici

        // Écrivez votre try / catch ici
    }
}
Voir la solution
Note.java·Java
public record Note(String matiere, double valeur) {

    public Note {
        if (valeur < 0 || valeur > 20) {
            throw new IllegalArgumentException("Note hors de 0 à 20 : " + valeur);
        }
    }

    public boolean estMoyenne() {
        return valeur >= 10;
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Note maths = new Note("Maths", 14.5);
        System.out.println(maths + " → moyenne : " + maths.estMoyenne());

        try {
            Note erreur = new Note("Histoire", 25);
            System.out.println(erreur);
        } catch (IllegalArgumentException e) {
            System.out.println("Refusée : " + e.getMessage());
        }
    }
}
Console
Note[matiere=Maths, valeur=14.5] → moyenne : true
Refusée : Note hors de 0 à 20 : 25.0
Ma confianceNon évalué
✓ Vérifiable

Exercice 23.5.B Du code en moins

Remplacez cette classe par un record, et adaptez le Main. Combien de lignes disparaissent ? Qu'est-ce que le record fait en plus ?

Coordonnees.java·Java
import java.util.Objects;

public class Coordonnees {
    private final double latitude;
    private final double longitude;

    public Coordonnees(double latitude, double longitude) {
        this.latitude = latitude;
        this.longitude = longitude;
    }

    public double getLatitude() { return latitude; }
    public double getLongitude() { return longitude; }

    @Override
    public boolean equals(Object autre) {
        if (!(autre instanceof Coordonnees c)) {
            return false;
        }
        return latitude == c.latitude && longitude == c.longitude;
    }

    @Override
    public int hashCode() { return Objects.hash(latitude, longitude); }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Coordonnees lyon = new Coordonnees(45.76, 4.84);
        System.out.println(lyon.getLatitude());
        System.out.println(lyon.equals(new Coordonnees(45.76, 4.84)));
    }
}
Voir la solution

Toute la classe tient en deux lignes. Dans le Main, getLatitude() devient latitude().

Coordonnees.java·Java
public record Coordonnees(double latitude, double longitude) {
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Coordonnees lyon = new Coordonnees(45.76, 4.84);
        System.out.println(lyon.latitude());
        System.out.println(lyon.equals(new Coordonnees(45.76, 4.84)));
        System.out.println(lyon);
    }
}
Console
45.76
true
Coordonnees[latitude=45.76, longitude=4.84]

En plus : un toString lisible, que la classe n'avait pas.

Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 23

  1. Que donne System.out.println(livre) si Livre ne redéfinit pas toString ?

  2. Sans redéfinition, que compare a.equals(b) ?

  3. Quelle signature redéfinit vraiment equals ?

  4. Une classe redéfinit equals mais pas hashCode. On ajoute deux objets égaux à un HashSet. Quelle taille ?

  5. Quelle règle est obligatoire ?

  6. Comment lit-on le titre de record Livre(String titre, String auteur) ?

  7. Où vérifier qu'un prix n'est pas négatif dans un record ?

  8. Pour un compte bancaire dont le solde change, on choisit plutôt…

1 gros exercice ou projet

Exercice 23.6.A Gros exercice : les cartes de fidélité (record + equals + toString)

Exercice de synthèse du chapitre. On gère des cartes de fidélité en réunissant tout le chapitre : un record pour la carte (données figées, toString/equals/hashCode gratuits), et l'usage de equals pour détecter les doublons dans une collection.

1. Le record. record Carte(String numero, String titulaire). Deux cartes sont « les mêmes » si elles ont le même numéro — or un record compare tous ses champs. On veut l'égalité sur le numéro seulement : redéfinissez equals et hashCode dans le record pour n'utiliser que numero.

2. L'anti-doublon. Dans main, créez une List<Carte>. Écrivez une méthode ajouter(List<Carte> liste, Carte c) qui n'ajoute la carte que si une carte de même numéro n'y est pas déjà (liste.contains(c) utilise votre equals).

3. Test. Ajoutez trois cartes dont deux ont le même numéro (titulaires différents), affichez la liste finale et sa taille.

Déroulé attendu :

Console
Ajoutée : Carte[numero=C-001, titulaire=Kevin]
Ajoutée : Carte[numero=C-002, titulaire=Léa]
Doublon ignoré : C-001
-----
2 carte(s) :
  Carte[numero=C-001, titulaire=Kevin]
  Carte[numero=C-002, titulaire=Léa]

Votre code à compléter

Java
import java.util.Objects;

public record Carte(String numero, String titulaire) {
    // Méthode à écrire : public boolean equals(Object o)
    // Méthode à écrire : public int hashCode()
}
Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    // Méthode à écrire : static void ajouter(List<Carte> liste, Carte c)

    public static void main(String[] args) {
        // Créez votre liste ici : cartes

        // Complétez ici

        // Affichez le résultat ici

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution

Un record compare tous ses champs par défaut. Ici on redéfinit equals et hashCode pour que seul le numero compte : deux cartes de même numéro sont « égales » même si le titulaire diffère. C'est ce equals que liste.contains(...) utilise pour détecter le doublon.

Java
import java.util.Objects;

public record Carte(String numero, String titulaire) {
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Carte autre)) return false;
        return this.numero.equals(autre.numero);   // égalité sur le numéro seulement
    }
    @Override
    public int hashCode() {
        return Objects.hash(numero);
    }
}
Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {

    static void ajouter(List<Carte> liste, Carte c) {
        if (liste.contains(c)) {                    // utilise notre equals (sur le numéro)
            System.out.println("Doublon ignoré : " + c.numero());
        } else {
            liste.add(c);
            System.out.println("Ajoutée : " + c);
        }
    }

    public static void main(String[] args) {
        List<Carte> cartes = new ArrayList<>();
        ajouter(cartes, new Carte("C-001", "Kevin"));
        ajouter(cartes, new Carte("C-002", "Léa"));
        ajouter(cartes, new Carte("C-001", "Hugo"));   // même numéro que Kevin

        System.out.println("-----");
        System.out.println(cartes.size() + " carte(s) :");
        for (Carte c : cartes) {
            System.out.println("  " + c);
        }
    }
}
Ma confianceNon évalué
✓ Vérifiable

Extras · 10 / 14

POO avancée

Chapitres 24 à 25

Chapitre 24 · POO avancée

Composition et énumérations

Objectif. Choisir entre héritage et composition, puis définir un ensemble fini de valeurs avec enum.

Glossaire du chapitre

« A-un »
Relation où un objet contient un autre : une voiture a un moteur.
Composition
Lien fort : l'objet contenu est créé par le tout et disparaît avec lui.
Agrégation
Lien faible : l'objet contenu existe indépendamment du tout.
enum
Type qui liste un nombre fixe de valeurs permises (FACILE, MOYEN, DIFFICILE).

5 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 24.1.B Un chat qui a une puce

Créez deux classes :

  1. Puce : un attribut numero et son getter ;
  2. Chat, qui la compose : le chat crée sa puce dans son constructeur, et une méthode sePresenter() affiche Chat <nom>, puce n°<numero>.

Votre code à compléter

Puce.java·Java
public class Puce {
    // Attributs : numero
    // Constructeur : Puce(int numero)
    // Méthode à écrire : public int getNumero()
}
Chat.java·Java
public class Chat {
    // Attributs : nom, puce
    // Constructeur : Chat(String nom, int numeroPuce)
    // Méthode à écrire : public void sePresenter()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez vos objets ici
    }
}
Voir la solution
Puce.java·Java
public class Puce {
    private int numero;

    public Puce(int numero) {
        this.numero = numero;
    }

    public int getNumero() {
        return numero;
    }
}
Chat.java·Java
public class Chat {
    private String nom;
    private Puce puce;

    public Chat(String nom, int numeroPuce) {
        this.nom = nom;
        this.puce = new Puce(numeroPuce);
    }

    public void sePresenter() {
        System.out.println("Chat " + nom + ", puce n°" + puce.getNumero());
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        new Chat("Félix", 4821).sePresenter();
    }
}
Console
Chat Félix, puce n°4821
Ma confianceNon évalué
✓ Vérifiable

Exercice 24.1.C Agrégation ou composition ?

Pour chaque couple, dites s'il s'agit plutôt d'une agrégation ou d'une composition :

  1. un livre et ses chapitres ;
  2. une classe d'école et ses élèves ;
  3. un corps humain et son cœur ;
  4. une playlist et ses chansons.
Voir la solution
  1. Composition : sans le livre, ses chapitres n'ont pas de sens.
  2. Agrégation : les élèves existent même si la classe est dissoute.
  3. Composition : le cœur ne vit pas sans le corps (dans le modèle).
  4. Agrégation : les chansons existent en dehors de la playlist.
Ma confianceNon évalué
Non vérifiable

Exercice 24.1.D Une facture et ses lignes

Une facture contient plusieurs lignes (un produit, une quantité, un prix unitaire). La facture ne calcule pas le prix de chaque ligne : elle demande à chacune son total, puis les additionne.

  1. Créez la classe LigneFacture : trois attributs privés produit (texte), quantite (entier) et prixUnitaire (décimal), un constructeur qui les reçoit, une méthode getTotal() qui renvoie quantite * prixUnitaire, et un toString() qui renvoie par exemple 3 x Cahier = 7.5 €.
  2. Créez la classe Facture : un attribut privé numero, un attribut lignes de type ArrayList<LigneFacture> créé vide dès la déclaration, et un constructeur qui reçoit le numéro. Ajoutez ajouterLigne(String produit, int quantite, double prixUnitaire), getTotal() (la somme des totaux des lignes) et afficher().
  3. Dans Main, créez la facture F-2026-014, ajoutez 3 cahiers à 2,5 € et 10 stylos à 1,2 €, puis affichez-la.
  4. Question : Facture et LigneFacture sont-elles liées par de la composition ou de l'agrégation ? Justifiez avec la règle « qui fait le new ? ».

Déroulé attendu :

Console
Facture F-2026-014
  3 x Cahier = 7.5 €
  10 x Stylo = 12.0 €
Total : 19.5 €

Votre code à compléter

LigneFacture.java·Java
public class LigneFacture {
    // Attributs : produit, quantite, prixUnitaire
    // Constructeur : LigneFacture(String produit, int quantite, double prixUnitaire)
    // Méthode à écrire : public double getTotal()
    // Méthode à écrire : public String toString()
}
Facture.java·Java
import java.util.ArrayList;

public class Facture {
    // Attributs : numero, lignes
    // Constructeur : Facture(String numero)
    // Méthode à écrire : public void ajouterLigne(String produit, int quantite, double prixUnitaire)
    // Méthode à écrire : public double getTotal()
    // Méthode à écrire : public void afficher()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre objet ici : f

        // Appelez la méthode ajouterLigne() ici

        // Appelez la méthode afficher() ici
    }
}
Voir la solution

Une Facture a des LigneFacture : on ne dit pas « une facture est une ligne », donc ce n'est pas de l'héritage mais un attribut. Chaque classe a un seul rôle : LigneFacture sait calculer le total d'une ligne, Facture sait additionner. La facture délègue le calcul de chaque ligne à la ligne elle-même.

LigneFacture.java·Java
public class LigneFacture {
    private String produit;
    private int quantite;
    private double prixUnitaire;

    public LigneFacture(String produit, int quantite, double prixUnitaire) {
        this.produit = produit;
        this.quantite = quantite;
        this.prixUnitaire = prixUnitaire;
    }

    public double getTotal() {
        return quantite * prixUnitaire;
    }

    @Override
    public String toString() {
        return quantite + " x " + produit + " = " + getTotal() + " €";
    }
}
Facture.java·Java
import java.util.ArrayList;

public class Facture {
    private String numero;
    private ArrayList<LigneFacture> lignes = new ArrayList<>();

    public Facture(String numero) {
        this.numero = numero;
    }

    public void ajouterLigne(String produit, int quantite, double prixUnitaire) {
        lignes.add(new LigneFacture(produit, quantite, prixUnitaire));
    }

    public double getTotal() {
        double total = 0;
        for (LigneFacture ligne : lignes) {
            total += ligne.getTotal();
        }
        return total;
    }

    public void afficher() {
        System.out.println("Facture " + numero);
        for (LigneFacture ligne : lignes) {
            System.out.println("  " + ligne);
        }
        System.out.println("Total : " + getTotal() + " €");
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Facture f = new Facture("F-2026-014");
        f.ajouterLigne("Cahier", 3, 2.5);
        f.ajouterLigne("Stylo", 10, 1.2);
        f.afficher();
    }
}

Réponse à la question : c'est de la composition. Les lignes sont fabriquées par la facture elle-même : le new LigneFacture(...) se trouve dans ajouterLigne. Personne ne passe à la facture une ligne déjà créée, et une ligne n'a aucun sens hors de sa facture : si la facture disparaît, ses lignes disparaissent avec elle.

Ma confianceNon évalué
✓ Vérifiable

Exercice 24.2.A Les saisons

Créez un enum Saison avec PRINTEMPS, ETE, AUTOMNE, HIVER, et affichez toutes les saisons avec values().

Votre code à compléter

Saison.java·Java
public enum Saison {
    // Écrivez votre code ici
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Saison.java·Java
public enum Saison {
    PRINTEMPS, ETE, AUTOMNE, HIVER
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        for (Saison s : Saison.values()) {
            System.out.println(s);
        }
    }
}
Console
PRINTEMPS
ETE
AUTOMNE
HIVER
Ma confianceNon évalué
✓ Vérifiable

Exercice 24.2.B Les niveaux de log (enum avec données)

Dans une application professionnelle, chaque message du journal (le « log ») a un niveau de gravité. On n'affiche que les messages dont la gravité est supérieure ou égale à un seuil choisi.

  1. Écrivez un enum NiveauLog avec quatre valeurs, chacune portant un entier gravite : DEBUG (0), INFO (1), WARN (2), ERROR (3). Ajoutez un getter getGravite().
  2. Dans Main, écrivez une méthode static void log(NiveauLog niveau, NiveauLog seuil, String message) : elle affiche [NIVEAU] message seulement si la gravité de niveau est supérieure ou égale à celle de seuil.
  3. Avec le seuil WARN, envoyez ces quatre messages : DEBUG « connexion ouverte », INFO « utilisateur Léa connecté », WARN « mot de passe faible », ERROR « base injoignable ».

Déroulé attendu :

Console
[WARN] mot de passe faible
[ERROR] base injoignable

Votre code à compléter

NiveauLog.java·Java
public enum NiveauLog {
    // Attributs : gravite
    // Constructeur : NiveauLog(int gravite)
    // Méthode à écrire : public int getGravite()
}
Main.java·Java
public class Main {
    // Méthode à écrire : static void log(NiveauLog niveau, NiveauLog seuil, String message)

    public static void main(String[] args) {
        // Déclarez votre variable ici : seuil

        // Complétez ici
    }
}
Voir la solution

La gravité est une donnée portée par chaque valeur de l'enum. La méthode compare ces nombres pour décider d'afficher ou non, sans jamais manipuler de texte.

NiveauLog.java·Java
public enum NiveauLog {
    DEBUG(0), INFO(1), WARN(2), ERROR(3);

    private final int gravite;

    NiveauLog(int gravite) {
        this.gravite = gravite;
    }

    public int getGravite() {
        return gravite;
    }
}
Main.java·Java
public class Main {

    static void log(NiveauLog niveau, NiveauLog seuil, String message) {
        if (niveau.getGravite() >= seuil.getGravite()) {
            System.out.println("[" + niveau + "] " + message);
        }
    }

    public static void main(String[] args) {
        NiveauLog seuil = NiveauLog.WARN;

        log(NiveauLog.DEBUG, seuil, "connexion ouverte");
        log(NiveauLog.INFO, seuil, "utilisateur Léa connecté");
        log(NiveauLog.WARN, seuil, "mot de passe faible");
        log(NiveauLog.ERROR, seuil, "base injoignable");
    }
}
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 24

  1. Une voiture a un moteur. Quel outil utiliser ?

  2. Dans une composition, que devient la partie si le tout disparaît ?

  3. Quel est l'intérêt d'un enum ?

Chapitre 25 · POO avancée

Interfaces avancées et patterns

Objectif. Enrichir une interface, créer une classe anonyme, et reconnaître un pattern célèbre : Builder.

Glossaire du chapitre

Classe imbriquée
Classe écrite à l'intérieur d'une autre ; déclarée static, elle n'a pas besoin de l'objet englobant (exemple : le Builder).
Méthode par défaut
Méthode d'interface avec le mot-clé default, fournie sans obligation de la réécrire.
Classe anonyme
Implémentation sans nom, écrite sur place pour un usage unique.
Design pattern
Solution classique à un problème classique de conception.
Builder
Pattern qui construit pas à pas un objet à nombreux attributs.

3 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 25.1.A Le paiement avec reçu (méthode default)

Vous gérez les moyens de paiement d'une boutique.

  1. Écrivez une interface Paiement avec une méthode abstraite void payer(double montant) et une méthode default void payerAvecRecu(double montant) qui appelle payer(montant) puis affiche Reçu envoyé pour 25.0 € (avec le montant payé).
  2. Écrivez Carte (son payer affiche Paiement par carte : 25.0 €) et Especes (son payer affiche Paiement en espèces : 25.0 €). Elles n'écrivent pas payerAvecRecu.
  3. Écrivez Cheque, dont le payer affiche Paiement par chèque : 25.0 €, et qui redéfinit payerAvecRecu pour afficher Pas de reçu pour un chèque après avoir payé.
  4. Dans main, appelez payerAvecRecu(25.0) sur une Carte, des Especes et un Cheque.

Déroulé attendu :

Console
Paiement par carte : 25.0 €
Reçu envoyé pour 25.0 €
Paiement en espèces : 25.0 €
Reçu envoyé pour 25.0 €
Paiement par chèque : 25.0 €
Pas de reçu pour un chèque

Votre code à compléter

Paiement.java·Java
public interface Paiement {
    // Méthode abstraite à déclarer : void payer(double montant)
    // Méthode à écrire : default void payerAvecRecu(double montant)
}
Carte.java·Java
public class Carte implements Paiement {
    // Méthode à écrire : public void payer(double montant)
}
Especes.java·Java
public class Especes implements Paiement {
    // Méthode à écrire : public void payer(double montant)
}
Cheque.java·Java
public class Cheque implements Paiement {
    // Méthode à écrire : public void payer(double montant)
    // Méthode à écrire : public void payerAvecRecu(double montant)
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : moyens

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution

Carte et Especes reçoivent payerAvecRecu gratuitement : la règle « payer puis envoyer un reçu » n'est écrite qu'une fois. Cheque a un besoin particulier, il redéfinit la méthode.

Paiement.java·Java
public interface Paiement {
    void payer(double montant);

    default void payerAvecRecu(double montant) {
        payer(montant);
        System.out.println("Reçu envoyé pour " + montant + " €");
    }
}
Carte.java·Java
public class Carte implements Paiement {

    @Override
    public void payer(double montant) {
        System.out.println("Paiement par carte : " + montant + " €");
    }
}
Especes.java·Java
public class Especes implements Paiement {

    @Override
    public void payer(double montant) {
        System.out.println("Paiement en espèces : " + montant + " €");
    }
}
Cheque.java·Java
public class Cheque implements Paiement {

    @Override
    public void payer(double montant) {
        System.out.println("Paiement par chèque : " + montant + " €");
    }

    @Override
    public void payerAvecRecu(double montant) {
        payer(montant);
        System.out.println("Pas de reçu pour un chèque");
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Paiement[] moyens = { new Carte(), new Especes(), new Cheque() };

        for (Paiement moyen : moyens) {
            moyen.payerAvecRecu(25.0);
        }
    }
}
Ma confianceNon évalué
✓ Vérifiable

Exercice 25.2.A Une classe anonyme

Avec l'interface Notifiable (fichier Notifiable.java de la section 25.2, à avoir dans votre projet) :

  1. créez une classe anonyme qui affiche Push : suivi du message ;
  2. appelez envoyerUrgent("Alerte").

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Complétez ici

        // Appelez la méthode envoyerUrgent() ici
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Notifiable push = new Notifiable() {
            @Override
            public void envoyer(String message) {
                System.out.println("Push : " + message);
            }
        };

        push.envoyerUrgent("Alerte");   // Push : URGENT : Alerte
    }
}
Console
Push : URGENT : Alerte
Ma confianceNon évalué
Non vérifiable

Exercice 25.4.A Un burger avec Builder

Créez une classe Burger avec un Builder : viande obligatoire, options avecFromage() et avecSalade(). Construisez un burger au bœuf avec fromage et affichez-le.

Votre code à compléter

Burger.java·Java
public class Burger {
    // Attributs : viande, fromage, salade
    // Constructeur : Burger(Builder b)
    // Méthode à écrire : public String toString()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Complétez ici

        // Affichez le résultat ici
    }
}
Voir la solution
Burger.java·Java
public class Burger {
    private final String viande;
    private final boolean fromage;
    private final boolean salade;

    private Burger(Builder b) {
        this.viande = b.viande;
        this.fromage = b.fromage;
        this.salade = b.salade;
    }

    @Override
    public String toString() {
        return "Burger " + viande + " [fromage=" + fromage + ", salade=" + salade + "]";
    }

    public static class Builder {
        private String viande;
        private boolean fromage;
        private boolean salade;

        public Builder(String viande) {
            this.viande = viande;
        }

        public Builder avecFromage() {
            this.fromage = true;
            return this;
        }

        public Builder avecSalade() {
            this.salade = true;
            return this;
        }

        public Burger build() {
            return new Burger(this);
        }
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Burger burger = new Burger.Builder("bœuf")
                .avecFromage()
                .build();

        System.out.println(burger);   // Burger bœuf [fromage=true, salade=false]
    }
}
Console
Burger bœuf [fromage=true, salade=false]
Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 25

  1. Que permet le mot-clé default dans une interface ?

  2. Quel est l'intérêt du pattern Builder ?

1 gros exercice ou projet

Exercice 25.4.B Gros exercice : la commande du restaurant (enum + composition + Builder)

Exercice de synthèse du chapitre. On modélise une commande de restaurant en réunissant trois outils du chapitre : une enum pour le statut, la composition (une commande contient des lignes) et le Builder pour construire la commande lisiblement. Les prix sont des nombres entiers (en euros).

1. L'enum. enum Statut { EN_PREPARATION, SERVIE, PAYEE }.

2. La composition. Une classe Ligne (plat : String, prix : int) avec ses getters. Une classe Commande qui contient une List<Ligne> et un Statut, avec une méthode total().

3. Le Builder. Un CommandeBuilder avec : ajouter(String plat, int prix) (renvoie le builder pour enchaîner), et build() (renvoie la Commande, au statut EN_PREPARATION).

4. Le main. Construire une commande avec trois plats en une seule chaîne d'appels, afficher le détail, le total, et le statut.

Déroulé attendu :

Console
Détail de la commande :
  Entrée : 6 €
  Plat : 14 €
  Dessert : 5 €
Total : 25 €
Statut : EN_PREPARATION

Votre code à compléter

Statut.java·Java
public enum Statut {
    // Écrivez votre code ici
}
Ligne.java·Java
public class Ligne {
    // Attributs : plat, prix
    // Constructeur : Ligne(String plat, int prix)
    // Méthode à écrire : public String getPlat()
    // Méthode à écrire : public int getPrix()
}
Commande.java·Java
import java.util.List;

public class Commande {
    // Attributs : lignes, statut
    // Constructeur : Commande(List<Ligne> lignes)
    // Méthode à écrire : public List<Ligne> getLignes()
    // Méthode à écrire : public Statut getStatut()
    // Méthode à écrire : public int total()
}
CommandeBuilder.java·Java
import java.util.ArrayList;
import java.util.List;

public class CommandeBuilder {
    // Attributs : lignes
    // Méthode à écrire : public CommandeBuilder ajouter(String plat, int prix)
    // Méthode à écrire : public Commande build()
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Complétez ici

        // Affichez le résultat ici

        // Parcourez avec une boucle for-each ici

        // Affichez le résultat ici
    }
}
Voir la solution

Le point clé du Builder : chaque ajouter(...) renvoie this, ce qui permet d'enchaîner les appels (.ajouter(...).ajouter(...).build()). La composition apparaît dans Commande, qui possède une List<Ligne>.

Statut.java·Java
public enum Statut {
    EN_PREPARATION, SERVIE, PAYEE
}
Ligne.java·Java
public class Ligne {
    private final String plat;
    private final int prix;

    public Ligne(String plat, int prix) {
        this.plat = plat;
        this.prix = prix;
    }

    public String getPlat() {
        return plat;
    }

    public int getPrix() {
        return prix;
    }
}
Commande.java·Java
import java.util.List;

public class Commande {
    private final List<Ligne> lignes;
    private Statut statut;

    public Commande(List<Ligne> lignes) {
        this.lignes = lignes;
        this.statut = Statut.EN_PREPARATION;
    }

    public List<Ligne> getLignes() {
        return lignes;
    }

    public Statut getStatut() {
        return statut;
    }

    public int total() {
        int total = 0;
        for (Ligne ligne : lignes) {
            total += ligne.getPrix();
        }
        return total;
    }
}
CommandeBuilder.java·Java
import java.util.ArrayList;
import java.util.List;

public class CommandeBuilder {
    private final List<Ligne> lignes = new ArrayList<>();

    public CommandeBuilder ajouter(String plat, int prix) {
        lignes.add(new Ligne(plat, prix));
        return this;
    }

    public Commande build() {
        return new Commande(lignes);
    }
}
Main.java·Java
public class Main {
    public static void main(String[] args) {
        Commande commande = new CommandeBuilder()
            .ajouter("Entrée", 6)
            .ajouter("Plat", 14)
            .ajouter("Dessert", 5)
            .build();

        System.out.println("Détail de la commande :");
        for (Ligne ligne : commande.getLignes()) {
            System.out.println("  " + ligne.getPlat() + " : " + ligne.getPrix() + " €");
        }
        System.out.println("Total : " + commande.total() + " €");
        System.out.println("Statut : " + commande.getStatut());
    }
}
Ma confianceNon évalué
✓ Vérifiable

Extras · 11 / 14

Le Java d'aujourd'hui

Chapitre 26

Chapitre 26 · Le Java d'aujourd'hui

Le Java moderne

Objectif. Écrire le Java des projets récents : var, le switch qui renvoie une valeur, les hiérarchies sealed et les motifs.

Glossaire du chapitre

var
Laisse Java deviner le type d'une variable locale à partir de la valeur donnée.
Switch fléché
Écriture case X -> … : pas de break, pas de cas qui « tombent » dans le suivant.
Switch expression
Un switch qui renvoie une valeur, que l'on peut affecter à une variable.
Interface sealed
Interface dont la liste des implémentations est fermée (permits).
Motif (pattern)
Forme testée dans un switch ou un instanceof pour reconnaître un type et en extraire le contenu.

2 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 26.3.A Moderniser un switch

Pour chaque mois de 1 à 12, affichez son nombre de jours :

  • 31 jours : janvier (1), mars (3), mai (5), juillet (7), août (8), octobre (10) et décembre (12) ;
  • 30 jours : avril (4), juin (6), septembre (9) et novembre (11) ;
  • 28 jours : février (2), sans tenir compte des années bissextiles.

Écrivez un switch expression avec des flèches, sans aucun break.

Votre code à compléter

Main.java·Java
public class Main {
    public static void main(String[] args) {
        // Écrivez votre boucle ici (for)
    }
}
Voir la solution
Main.java·Java
public class Main {
    public static void main(String[] args) {
        for (int mois = 1; mois <= 12; mois++) {
            int jours = switch (mois) {
                case 2 -> 28;
                case 4, 6, 9, 11 -> 30;
                default -> 31;
            };
            System.out.println("Mois " + mois + " : " + jours + " jours");
        }
    }
}
Console
Mois 1 : 31 jours
Mois 2 : 28 jours
Mois 3 : 31 jours
Mois 4 : 30 jours
Mois 5 : 31 jours
Mois 6 : 30 jours
Mois 7 : 31 jours
Mois 8 : 31 jours
Mois 9 : 30 jours
Mois 10 : 31 jours
Mois 11 : 30 jours
Mois 12 : 31 jours
Ma confianceNon évalué
✓ Vérifiable

Exercice 26.6.A Les formes, version moderne

Reprenez l'exercice 19.5.A, en version moderne :

  • une interface sealed Forme ;
  • deux records : Rectangle(double largeur, double longueur) et Cercle(double rayon) ;
  • une méthode static double aire(Forme f) avec un switch sur le type, sans default.

Affichez chaque forme et son aire.

Votre code à compléter

Rectangle.java·Java
public record Rectangle(double largeur, double longueur) implements Forme {
    // Écrivez votre code ici
}
Cercle.java·Java
public record Cercle(double rayon) implements Forme {
    // Écrivez votre code ici
}
Main.java·Java
public class Main {
    // Méthode à écrire : static double aire(Forme f)

    public static void main(String[] args) {
        // Créez votre tableau ici : formes

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Forme.java·Java
public sealed interface Forme permits Rectangle, Cercle {
}
Rectangle.java·Java
public record Rectangle(double largeur, double longueur) implements Forme {
}
Cercle.java·Java
public record Cercle(double rayon) implements Forme {
}
Main.java·Java
public class Main {
    static double aire(Forme f) {
        return switch (f) {
            case Rectangle r -> r.largeur() * r.longueur();
            case Cercle c -> Math.PI * c.rayon() * c.rayon();
        };
    }

    public static void main(String[] args) {
        Forme[] formes = { new Rectangle(4, 5), new Cercle(2) };

        for (Forme f : formes) {
            System.out.println(f + " -> aire : " + aire(f));
        }
    }
}
Console
Rectangle[largeur=4.0, longueur=5.0] -> aire : 20.0
Cercle[rayon=2.0] -> aire : 12.566370614359172

Le toString() automatique du record affiche ses champs.

Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 26

  1. Quel est le type de nom après var nom = "Léa"; ?

  2. Laquelle de ces lignes compile ?

  3. Avec case 6, 7 -> …, faut-il un break ?

  4. Dans un switch expression, à quoi sert yield ?

  5. Que permet if (objet instanceof String s) ?

  6. Que fait sealed interface Paiement permits Carte, Especes ?

  7. Un switch sur une interface sealed oublie un cas. Que se passe-t-il ?

  8. Dans un switch sur des types, que fait case CarteCadeau cc when cc.solde() < montant ?

1 gros exercice ou projet

Exercice 26.6.B Gros exercice : les moyens de paiement (sealed + switch sur types + record)

Exercice de synthèse du chapitre, 100 % Java moderne. On calcule les frais selon le moyen de paiement, en réunissant : une interface sealed, des record pour chaque moyen, et un switch sur les types que le compilateur vérifie comme exhaustif.

1. Le contrat scellé. sealed interface Paiement permits Carte, Especes, Cheque.

2. Trois moyens (records). record Carte(double montant), record Especes(double montant), record Cheque(double montant).

3. Les frais. Une méthode frais(Paiement p) qui renvoie, avec un switch sur le type : carte = 1 % du montant, espèces = 0, chèque = 0,50 € fixe.

4. Le main. Calculez et affichez les frais pour un paiement de chaque type.

Déroulé attendu :

Console
Carte[montant=200.0] frais : 2,00 €
Especes[montant=50.0] frais : 0,00 €
Cheque[montant=120.0] frais : 0,50 €

Votre code à compléter

Main.java·Java
public class Main {
    // Méthode à écrire : static double frais(Paiement p)

    public static void main(String[] args) {
        // Complétez ici

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution

Le point fort : comme Paiement est sealed sur exactement trois types, le switch sait qu'il n'y a que ces trois cas. Si on oubliait Cheque, le programme ne compilerait pas (« switch non exhaustif »). Chaque case reçoit l'objet déjà casté — plus besoin de instanceof ni de cast manuel.

Java
public sealed interface Paiement permits Carte, Especes, Cheque {
    double montant();
}
Java
public record Carte(double montant) implements Paiement {}
Java
public record Especes(double montant) implements Paiement {}
Java
public record Cheque(double montant) implements Paiement {}
Main.java·Java
public class Main {

    static double frais(Paiement p) {
        return switch (p) {
            case Carte c    -> c.montant() * 0.01;   // 1 %
            case Especes e  -> 0.0;                   // gratuit
            case Cheque ch  -> 0.50;                  // forfait
        };
        // pas de "default" : le compilateur sait que les 3 cas couvrent tout
    }

    public static void main(String[] args) {
        Paiement[] paiements = {
            new Carte(200.0),
            new Especes(50.0),
            new Cheque(120.0)
        };
        for (Paiement p : paiements) {
            System.out.printf("%-20s frais : %.2f €%n", p, frais(p));
        }
    }
}
Ma confianceNon évalué
✓ Vérifiable

Extras · 12 / 14

Passer de PHP à Java

Chapitre 27

Chapitre 27 · Passer de PHP à Java

De PHP à Java : les pièges

Objectif. Repérer les réflexes de PHP qui trompent en Java : la copie des tableaux, ==, les nombres, null et ce que PHP n'a pas.

Glossaire du chapitre

Copie superficielle
Copie qui duplique la « boîte » mais pas son contenu : les éléments sont toujours partagés avec l'original.
Référence
L'adresse d'un objet en mémoire. Affecter un tableau ou un objet à une autre variable copie la référence, pas l'objet.
== et equals
Pour les types primitifs, == compare les valeurs. Pour les objets (dont String), == compare les références : on utilise equals pour comparer le contenu.
Division entière
Entre deux int, / donne un entier et supprime la partie décimale : 7 / 2 vaut 3.
Dépassement
Quand un entier dépasse la valeur maximale de son type, il « repart » de l'autre côté sans aucune erreur.
null
Valeur qui signifie « aucun objet ». Appeler une méthode dessus provoque une NullPointerException.
Typage statique
Chaque variable a un type fixe, vérifié par le compilateur avant l'exécution.
Exception vérifiée
Exception que le compilateur oblige à gérer (try / catch) ou à déclarer (throws).
Surcharge
Plusieurs méthodes de même nom mais de paramètres différents.

6 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 27.2.A Une commande qui grossit toute seule

La méthode avecCafe doit renvoyer une nouvelle liste, sans toucher à la commande d'origine. Pourtant la commande d'origine contient aussi le café. Corrigez la méthode.

Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    // Doit renvoyer une COPIE de la liste, avec "café" en plus
    static List<String> avecCafe(List<String> commande) {
        List<String> copie = commande;
        copie.add("café");
        return copie;
    }

    public static void main(String[] args) {
        List<String> commande = new ArrayList<>();
        commande.add("croissant");

        List<String> grande = avecCafe(commande);
        System.out.println("Commande : " + commande);
        System.out.println("Grande commande : " + grande);
    }
}
Console
Commande : [croissant, café]
Grande commande : [croissant, café]
Voir la solution

copie = commande copie la référence, pas la liste. On crée une vraie copie avec new ArrayList<>(commande) :

Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    // Doit renvoyer une COPIE de la liste, avec "café" en plus
    static List<String> avecCafe(List<String> commande) {
        List<String> copie = new ArrayList<>(commande);
        copie.add("café");
        return copie;
    }

    public static void main(String[] args) {
        List<String> commande = new ArrayList<>();
        commande.add("croissant");

        List<String> grande = avecCafe(commande);
        System.out.println("Commande : " + commande);
        System.out.println("Grande commande : " + grande);
    }
}
Console
Commande : [croissant]
Grande commande : [croissant, café]
Ma confianceNon évalué
✓ Vérifiable

Exercice 27.3.A Égalité des scores

Ce programme annonce « Scores différents » alors que le premier et le dernier score valent tous deux 250. Trouvez le réflexe PHP et corrigez-le.

Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> scores = new ArrayList<>();
        scores.add(250);
        scores.add(180);
        scores.add(250);

        Integer premier = scores.get(0);
        Integer dernier = scores.get(2);
        if (premier == dernier) {
            System.out.println("Égalité !");
        } else {
            System.out.println("Scores différents");
        }
    }
}
Console
Scores différents
Voir la solution

premier et dernier sont des Integer, des objets : == compare les références, et 250 est hors du cache de -128 à 127. On compare les contenus :

Main.java·Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> scores = new ArrayList<>();
        scores.add(250);
        scores.add(180);
        scores.add(250);

        Integer premier = scores.get(0);
        Integer dernier = scores.get(2);
        if (premier.equals(dernier)) {
            System.out.println("Égalité !");
        } else {
            System.out.println("Scores différents");
        }
    }
}
Console
Égalité !
Ma confianceNon évalué
✓ Vérifiable

Exercice 27.3.B Une moyenne arrondie

La moyenne de 12, 15 et 16 vaut 14,33… mais le programme affiche 14.0, alors que la méthode renvoie un double. Pourquoi ? Corrigez.

Main.java·Java
public class Main {
    static double moyenne(int[] notes) {
        int total = 0;
        for (int note : notes) {
            total += note;
        }
        return total / notes.length;
    }

    public static void main(String[] args) {
        int[] notes = {12, 15, 16};
        System.out.println("Moyenne : " + moyenne(notes));
    }
}
Console
Moyenne : 14.0
Voir la solution

total / notes.length divise deux int : le résultat est déjà 14 avant d'être converti en double. On convertit avant de diviser :

Main.java·Java
public class Main {
    static double moyenne(int[] notes) {
        int total = 0;
        for (int note : notes) {
            total += note;
        }
        return (double) total / notes.length;
    }

    public static void main(String[] args) {
        int[] notes = {12, 15, 16};
        System.out.println("Moyenne : " + moyenne(notes));
    }
}
Console
Moyenne : 14.333333333333334
Ma confianceNon évalué
✓ Vérifiable

Exercice 27.4.A Le stock de la boulangerie

Le programme s'arrête sur la brioche, absente du stock. En PHP, $stock['brioche'] ?? 0 aurait suffi. Faites afficher brioche : 0.

Main.java·Java
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> stock = new HashMap<>();
        stock.put("baguette", 30);
        stock.put("croissant", 12);

        String[] produits = {"baguette", "brioche", "croissant"};
        for (String produit : produits) {
            int quantite = stock.get(produit);
            System.out.println(produit + " : " + quantite);
        }
    }
}
Console
baguette : 30
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.get(Object)" is null
	at Main.main(Main.java:12)
Voir la solution

stock.get("brioche") renvoie null, et Java essaie de le convertir en int : c'est la NPE. On garde un Integer, et on teste null :

Main.java·Java
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> stock = new HashMap<>();
        stock.put("baguette", 30);
        stock.put("croissant", 12);

        String[] produits = {"baguette", "brioche", "croissant"};
        for (String produit : produits) {
            // get renvoie null pour une clé absente : PHP aurait écrit $stock[$produit] ?? 0
            Integer quantite = stock.get(produit);
            if (quantite == null) {
                quantite = 0;
            }
            System.out.println(produit + " : " + quantite);
        }
    }
}
Console
baguette : 30
brioche : 0
croissant : 12

La méthode stock.getOrDefault(produit, 0) fait la même chose en une ligne ; elle a été présentée à la section 11.6.

Ma confianceNon évalué
✓ Vérifiable

Exercice 27.4.B int ou Integer ? Gérer une valeur absente

Un petit annuaire associe des prénoms à des âges : Léa → 29 et Hugo → 41.

  1. Écrivez static Integer chercherAge(Map<String, Integer> annuaire, String prenom) qui renvoie l'âge, ou null si le prénom est inconnu.
  2. Dans main, cherchez Léa puis Zoé et affichez, pour chacun :
    • <prénom> : <âge> ans ;
    • ou <prénom> : âge inconnu.

Pourquoi le type de retour est-il Integer et non int ? Que se passerait-il avec int, pour Zoé ?

Votre code à compléter

Main.java·Java
import java.util.HashMap;
import java.util.Map;

public class Main {
    // Méthode à écrire : static Integer chercherAge(Map<String, Integer> annuaire, String prenom)

    public static void main(String[] args) {
        // Créez votre dictionnaire (HashMap) ici : annuaire

        // Appelez la méthode put() ici

        // Créez votre tableau ici : recherches

        // Parcourez avec une boucle for-each ici
    }
}
Voir la solution
Main.java·Java
import java.util.HashMap;
import java.util.Map;

public class Main {

    static Integer chercherAge(Map<String, Integer> annuaire, String prenom) {
        return annuaire.get(prenom);          // null si la clé n'existe pas
    }

    public static void main(String[] args) {
        Map<String, Integer> annuaire = new HashMap<>();
        annuaire.put("Léa", 29);
        annuaire.put("Hugo", 41);

        String[] recherches = {"Léa", "Zoé"};
        for (String prenom : recherches) {
            Integer age = chercherAge(annuaire, prenom);
            if (age == null) {
                System.out.println(prenom + " : âge inconnu");
            } else {
                System.out.println(prenom + " : " + age + " ans");
            }
        }
    }
}
Console
Léa : 29 ans
Zoé : âge inconnu

Avec un retour int, le null renvoyé par annuaire.get("Zoé") devrait être converti en int : Java lèverait une NullPointerException à l'intérieur de chercherAge. Un int ne pouvant pas dire « pas de valeur », on utilise Integer, et l'appelant teste null avant d'utiliser le nombre.

Ma confianceNon évalué
✓ Vérifiable

Exercice 27.5.A Trois façons de donner un prix

Écrivez trois méthodes prix qui renvoient un texte :

  • prix(1250) reçoit des centimes ;
  • prix(12.5) reçoit des euros ;
  • prix("1250") reçoit des centimes écrits en texte.

Les trois doivent afficher 12,50 €.

Votre code à compléter

Main.java·Java
public class Main {
    // Méthode à écrire : static String prix(int centimes)
    // Méthode à écrire : static String prix(double euros)
    // Méthode à écrire : static String prix(String texte)

    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution

Trois méthodes du même nom, que le compilateur distingue par le type du paramètre. La version String réutilise la version int :

Main.java·Java
public class Main {
    static String prix(int centimes) {
        return (centimes / 100) + "," + String.format("%02d", centimes % 100) + " €";
    }

    static String prix(double euros) {
        return String.format("%.2f €", euros);
    }

    static String prix(String texte) {
        return prix(Integer.parseInt(texte));
    }

    public static void main(String[] args) {
        System.out.println(prix(1250));
        System.out.println(prix(12.5));
        System.out.println(prix("1250"));
    }
}
Console
12,50 €
12,50 €
12,50 €

%02d écrit l'entier sur deux chiffres (5 devient 05) ; %.2f écrit deux décimales, avec la virgule si l'ordinateur est réglé en français.

Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 27

  1. En Java, une méthode reçoit une ArrayList et lui ajoute un élément. Que voit l'appelant ?

  2. Integer a = 500; Integer b = 500; Que vaut a == b ?

  3. Que vaut "5" + 3 en Java ?

  4. Que se passe-t-il avec int n = Integer.MAX_VALUE + 1; ?

  5. List<String> panier = new ArrayList<>(); Que fait if (panier) { … } ?

  6. Quel est l'équivalent de $ville ?? "inconnue" ?

  7. Deux méthodes afficher(int x) et afficher(double x). Que fait afficher(7 / 2) ?

  8. Un fichier Main.java contient public class Main et public class Client. Que se passe-t-il ?

  9. Peut-on écrire int age = null; ?

  10. Que se passe-t-il avec Integer a = null; int b = a; ?

1 gros exercice ou projet

Exercice 27.6.A Gros exercice : la chasse aux pièges (spécial PHP → Java)

Exercice de synthèse du chapitre. Voici un programme écrit par un développeur qui vient de PHP. Il compile, mais il contient quatre pièges vus dans le chapitre, et il n'affiche pas ce que son auteur croyait. Pour chacun : dites ce qui s'affiche vraiment, pourquoi, et corrigez.

Main.java·Java
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {

    static void ajouterBonus(List<Integer> notes) {
        notes.add(20);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // Piège 1 : comparer du texte
        System.out.print("Rôle : ");
        String role = scanner.nextLine();
        if (role == "admin") {
            System.out.println("Accès autorisé");
        } else {
            System.out.println("Accès refusé");
        }

        // Piège 2 : division entière
        int total = 45;
        int nb = 2;
        double moyenne = total / nb;
        System.out.println("Moyenne : " + moyenne);

        // Piège 3 : l'objet partagé
        List<Integer> notes = new ArrayList<>();
        notes.add(12);
        notes.add(15);
        ajouterBonus(notes);
        System.out.println("Notes : " + notes);

        // Piège 4 : null
        String ville = null;
        System.out.println("Ville : " + ville.toUpperCase());
    }
}

Le programme est lancé avec la saisie suivante :

⌨ Valeur à saisir

Pour chaque piège : quelle ligne pose problème ? qu'affiche (ou que fait) réellement le programme ? comment corriger ?

Voir la solution

Piège 1 : == sur du texte (ligne if (role == "admin")). Un texte saisi au clavier est un objet distinct du texte écrit dans le code : == compare les adresses, pas le contenu. Même en tapant admin, le programme affiche « Accès refusé ». Correct : if (role.equals("admin")).

Piège 2 : division entière (total / nb). 45 / 2 donne 22 (entier), pas 22.5 : la division est faite avant la conversion en double. On lit « Moyenne : 22.0 ». Correct : double moyenne = (double) total / nb; donne 22.5.

Piège 3 : l'objet est partagé, pas copié. ajouterBonus(notes) modifie la vraie liste : après l'appel, notes vaut [12, 15, 20], pas [12, 15]. Ce n'est pas un bug si c'est voulu, mais il faut en être conscient : la méthode reçoit une copie de la référence, donc elle ouvre la même liste.

Piège 4 : NullPointerException. ville vaut null, donc ville.toUpperCase() plante à l'exécution (le programme s'arrête ici). Correct : tester ville != null avant, ou donner une valeur par défaut avec Objects.requireNonNullElse.

Voici la version corrigée et ce qu'elle affiche avec la même saisie :

⌨ Valeur à saisir

Main.java·Java
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Scanner;

public class Main {

    static void ajouterBonus(List<Integer> notes) {
        notes.add(20);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("Rôle : ");
        String role = scanner.nextLine();
        if (role.equals("admin")) {
            System.out.println("Accès autorisé");
        } else {
            System.out.println("Accès refusé");
        }

        int total = 45;
        int nb = 2;
        double moyenne = (double) total / nb;
        System.out.println("Moyenne : " + moyenne);

        List<Integer> notes = new ArrayList<>();
        notes.add(12);
        notes.add(15);
        ajouterBonus(notes);
        System.out.println("Notes : " + notes);

        String ville = null;
        String affichee = Objects.requireNonNullElse(ville, "inconnue");
        System.out.println("Ville : " + affichee);
    }
}
Console
Rôle : admin
Accès autorisé
Moyenne : 22.5
Notes : [12, 15, 20]
Ville : inconnue
Ma confianceNon évalué
✓ Vérifiable

Extras · 13 / 14

Les outils du quotidien

Chapitres 28 à 29

Chapitre 28 · Les outils du quotidien

Les collections, plus loin

Objectif. Choisir la bonne collection, se servir des méthodes utiles de Map, et éviter les erreurs classiques : l'ordre perdu et la liste immuable.

Glossaire du chapitre

Programmer vers l'interface
Déclarer la variable avec le contrat (List, Map) et choisir la classe à droite (ArrayList, HashMap).
TreeMap / TreeSet
Collections toujours triées (par ordre naturel des clés ou des éléments).
Ordre d'une HashMap
Aucun ordre garanti : l'ordre d'affichage peut surprendre.
getOrDefault
Lit la valeur d'une clé, ou une valeur par défaut si la clé n'existe pas.
merge
Combine une nouvelle valeur avec l'ancienne d'une clé (pratique pour compter).
Collection immuable
Créée par List.of ou Map.of : toute modification lève une exception.
Collections
Classe d'outils pour les collections, par exemple Collections.sort(liste).

3 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 28.4.A Compter les mots

Comptez les mots de la phrase "le chat voit le chien et le chat fuit" :

  • découpez-la avec split(" ") ;
  • affichez chaque mot avec son nombre, dans l'ordre de première apparition.

Votre code à compléter

Main.java·Java
import java.util.LinkedHashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : phrase

        // Créez votre dictionnaire (HashMap) ici : compte

        // Parcourez avec une boucle for-each ici

        // Écrivez votre boucle ici (for)
    }
}
Voir la solution

Une LinkedHashMap pour l'ordre, getOrDefault pour le premier comptage :

Main.java·Java
import java.util.LinkedHashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        String phrase = "le chat voit le chien et le chat fuit";
        Map<String, Integer> compte = new LinkedHashMap<>();

        for (String mot : phrase.split(" ")) {
            compte.put(mot, compte.getOrDefault(mot, 0) + 1);
        }

        for (Map.Entry<String, Integer> e : compte.entrySet()) {
            System.out.println(e.getKey() + " : " + e.getValue());
        }
    }
}
Console
le : 3
chat : 2
voit : 1
chien : 1
et : 1
fuit : 1
Ma confianceNon évalué
✓ Vérifiable

Exercice 28.4.B Un annuaire trié

Créez un annuaire prénom → téléphone avec Zoé, Adam et Malik, ajoutés dans cet ordre.

  1. Affichez-le trié par prénom, une ligne par personne.
  2. Cherchez ensuite Inès et affichez son numéro, ou un message si elle n'y est pas.

Votre code à compléter

Main.java·Java
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        // Créez votre dictionnaire (HashMap) ici : annuaire

        // Appelez la méthode put() ici

        // Écrivez votre boucle ici (for)

        // Déclarez votre variable ici : cherche

        // Écrivez votre condition ici (if / else)
    }
}
Voir la solution
Main.java·Java
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        Map<String, String> annuaire = new TreeMap<>();
        annuaire.put("Zoé", "06 10 10 10 10");
        annuaire.put("Adam", "06 20 20 20 20");
        annuaire.put("Malik", "06 30 30 30 30");

        for (Map.Entry<String, String> e : annuaire.entrySet()) {
            System.out.println(e.getKey() + " : " + e.getValue());
        }

        String cherche = "Inès";
        if (annuaire.containsKey(cherche)) {
            System.out.println(cherche + " : " + annuaire.get(cherche));
        } else {
            System.out.println(cherche + " n'est pas dans l'annuaire");
        }
    }
}
Console
Adam : 06 20 20 20 20
Malik : 06 30 30 30 30
Zoé : 06 10 10 10 10
Inès n'est pas dans l'annuaire
Ma confianceNon évalué
✓ Vérifiable

Exercice 28.4.C L'inventaire de la boutique

Une boutique reçoit des livraisons sous forme de textes "produit:quantité" : "stylo:10", "cahier:4", "stylo:5", "gomme:3", "cahier:6".

  1. Rangez les quantités dans une Map<String, Integer>, en additionnant les livraisons d'un même produit (avec getOrDefault).
  2. Affichez l'inventaire, un produit par ligne, dans l'ordre alphabétique (indice : TreeMap).

Pour chaque livraison, texte.split(":") donne un tableau de deux cases, et Integer.parseInt convertit la quantité.

Console
cahier : 10
gomme : 3
stylo : 15

Votre code à compléter

Main.java·Java
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        // Créez votre tableau ici : livraisons

        // Créez votre dictionnaire (HashMap) ici : inventaire

        // Parcourez avec une boucle for-each ici

        // Écrivez votre boucle ici (for)
    }
}
Voir la solution
Main.java·Java
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        String[] livraisons = {"stylo:10", "cahier:4", "stylo:5", "gomme:3", "cahier:6"};

        Map<String, Integer> inventaire = new TreeMap<>();     // trié par clé

        for (String livraison : livraisons) {
            String[] morceaux = livraison.split(":");
            String produit = morceaux[0];
            int quantite = Integer.parseInt(morceaux[1]);

            inventaire.put(produit, inventaire.getOrDefault(produit, 0) + quantite);
        }

        for (Map.Entry<String, Integer> ligne : inventaire.entrySet()) {
            System.out.println(ligne.getKey() + " : " + ligne.getValue());
        }
    }
}
Console
cahier : 10
gomme : 3
stylo : 15

getOrDefault(produit, 0) évite le null d'un produit pas encore rencontré (section 27.4). Avec un HashMap, l'ordre d'affichage ne serait pas garanti ; le TreeMap trie par clé.

Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 28

  1. Dans quel ordre une HashMap affiche-t-elle ses clés ?

  2. Quelle collection Java se comporte comme un tableau associatif PHP ?

  3. Que rend stock.getOrDefault("Flan", 0) si la clé "Flan" n'existe pas ?

  4. On ajoute un objet d'une classe qui n'implémente pas Comparable dans un TreeSet sans Comparator. Que se passe-t-il ?

  5. List<String> jours = List.of("Lundi"); jours.add("Mardi"); donne…

1 gros exercice ou projet

Exercice 28.5.A Gros exercice : le best-seller de la boulangerie (Map + tri)

Exercice de synthèse du chapitre. À partir d'une liste de ventes, on calcule les quantités par produit, on les affiche triées par nom, et on trouve le best-seller. On réunit getOrDefault et TreeMap.

On part de cette liste de ventes (un produit vendu = une entrée) :

Console
["baguette", "croissant", "baguette", "pain", "baguette", "croissant"]

1. Compter. Avec une Map<String, Integer> et getOrDefault, comptez combien de fois chaque produit est vendu.

2. Afficher trié. Affichez les quantités par ordre alphabétique de produit (indice : TreeMap).

3. Le best-seller. Trouvez le produit le plus vendu et affichez-le (indice : un parcours des entrées en gardant le maximum).

Déroulé attendu :

Console
Ventes par produit :
  baguette : 3
  croissant : 2
  pain : 1
Best-seller : baguette (3 vendus)

Votre code à compléter

Main.java·Java
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        // Complétez ici

        // Créez votre dictionnaire (HashMap) ici : quantites

        // Parcourez avec une boucle for-each ici

        // Affichez le résultat ici

        // Écrivez votre boucle ici (for)

        // Complétez ici

        // Déclarez votre variable ici : best

        // Déclarez votre variable ici : maxQte

        // Écrivez votre boucle ici (for)

        // Affichez le résultat ici
    }
}
Voir la solution

Deux idées clés : getOrDefault(produit, 0) évite tout test de clé absente pour compter ; une TreeMap donne l'affichage trié gratuitement, sans trier soi-même. Pour le best-seller, on parcourt les entrées en gardant le maximum.

Main.java·Java
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        List<String> ventes = List.of(
            "baguette", "croissant", "baguette", "pain", "baguette", "croissant"
        );

        // 1 + 2 : compter dans une TreeMap => déjà trié par nom
        Map<String, Integer> quantites = new TreeMap<>();
        for (String produit : ventes) {
            quantites.put(produit, quantites.getOrDefault(produit, 0) + 1);
        }

        System.out.println("Ventes par produit :");
        for (Map.Entry<String, Integer> e : quantites.entrySet()) {
            System.out.println("  " + e.getKey() + " : " + e.getValue());
        }

        // 3 : le best-seller
        String best = null;
        int maxQte = 0;
        for (Map.Entry<String, Integer> e : quantites.entrySet()) {
            if (e.getValue() > maxQte) {
                maxQte = e.getValue();
                best = e.getKey();
            }
        }
        System.out.println("Best-seller : " + best + " (" + maxQte + " vendus)");
    }
}
Ma confianceNon évalué
✓ Vérifiable

Chapitre 29 · Les outils du quotidien

Les fichiers, et les dates plus loin

Objectif. Aller plus loin avec les dates (heure, écart entre deux dates), puis lire et écrire des fichiers texte, jusqu'à transformer un CSV en objets.

Glossaire du chapitre

LocalTime / LocalDateTime
Une heure seule, ou une date avec son heure (2026-10-19T11:20).
Period / ChronoUnit
Mesurent un écart entre deux dates (en années-mois-jours, ou dans une seule unité).
DateTimeFormatter
Définit la forme d'une date pour l'afficher (dd/MM/yyyy).
Path et Files
Représentent un chemin de fichier et fournissent les méthodes pour le lire ou l'écrire.
IOException
Exception vérifiée levée quand la lecture ou l'écriture d'un fichier échoue.
CSV
Fichier texte où chaque ligne contient des valeurs séparées par un caractère (souvent ;).

9 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 29.2.A L'âge exact

Une personne est née le 15 juillet 1990. À la date du 19 octobre 2026, affichez :

  1. son âge sous la forme 36 ans, 3 mois et 4 jours (avec Period.between) ;
  2. le nombre de jours vécus (avec ChronoUnit.DAYS.between).

On fixe la date du jour pour que le résultat soit toujours le même (sinon, on écrirait LocalDate.now()).

Console
36 ans, 3 mois et 4 jours
13245 jours vécus

Votre code à compléter

Main.java·Java
import java.time.LocalDate;
import java.time.Period;
import java.time.temporal.ChronoUnit;

public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : naissance

        // Déclarez votre variable ici : aujourdhui

        // Déclarez votre variable ici : age

        // Affichez le résultat ici

        // Déclarez votre variable ici : jours

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.time.LocalDate;
import java.time.Period;
import java.time.temporal.ChronoUnit;

public class Main {
    public static void main(String[] args) {
        LocalDate naissance = LocalDate.of(1990, 7, 15);
        LocalDate aujourdhui = LocalDate.of(2026, 10, 19);      // en vrai : LocalDate.now()

        Period age = Period.between(naissance, aujourdhui);
        System.out.println(age.getYears() + " ans, " + age.getMonths() + " mois et " + age.getDays() + " jours");

        long jours = ChronoUnit.DAYS.between(naissance, aujourdhui);
        System.out.println(jours + " jours vécus");
    }
}
Console
36 ans, 3 mois et 4 jours
13245 jours vécus

Period découpe l'écart en années, mois et jours ; ChronoUnit.DAYS.between le compte dans une seule unité.

Ma confianceNon évalué
✓ Vérifiable

Exercice 29.2.B Le livre à rendre

Un livre emprunté le 19/10/2026 doit être rendu 21 jours plus tard. Nous sommes le 02/11/2026 (créez-les avec LocalDate.of). Affichez :

  1. la date limite, au format français (dd/MM/yyyy) ;
  2. le nombre de jours restants, ou le nombre de jours de retard.

Votre code à compléter

Main.java·Java
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class Main {
    public static void main(String[] args) {
        // Calculez ici : francais

        // Déclarez votre variable ici : emprunt

        // Déclarez votre variable ici : aujourdhui

        // Déclarez votre variable ici : limite

        // Déclarez votre variable ici : reste

        // Affichez le résultat ici

        // Écrivez votre condition ici (if / else)
    }
}
Voir la solution
Main.java·Java
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter francais = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        LocalDate emprunt = LocalDate.of(2026, 10, 19);
        LocalDate aujourdhui = LocalDate.of(2026, 11, 2);

        LocalDate limite = emprunt.plusDays(21);
        long reste = ChronoUnit.DAYS.between(aujourdhui, limite);

        System.out.println("À rendre le " + limite.format(francais));
        if (reste >= 0) {
            System.out.println("Il reste " + reste + " jours");
        } else {
            System.out.println("En retard de " + (-reste) + " jours");
        }
    }
}
Console
À rendre le 09/11/2026
Il reste 7 jours

Essayez avec LocalDate.of(2026, 11, 15) comme date du jour : le programme affiche En retard de 6 jours.

Ma confianceNon évalué
✓ Vérifiable

Exercice 29.2.C Le planning de la semaine

Affichez les 7 jours de la semaine qui commence le lundi 19 octobre 2026 :

  • un jour par ligne, sous la forme lundi 19/10 ;
  • ajoutez (week-end) pour le samedi et le dimanche.

Indices : plusDays(i) dans une boucle ; un tableau {"lundi", "mardi", …} donne le nom du jour ; le format "dd/MM" donne le jour et le mois ; getDayOfWeek() renvoie un DayOfWeek (comparez avec DayOfWeek.SATURDAY et DayOfWeek.SUNDAY).

Console
lundi 19/10
mardi 20/10
mercredi 21/10
jeudi 22/10
vendredi 23/10
samedi 24/10 (week-end)
dimanche 25/10 (week-end)

Votre code à compléter

Main.java·Java
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : lundi

        // Calculez ici : format

        // Créez votre tableau ici : noms

        // Écrivez votre boucle ici (for)
    }
}
Voir la solution
Main.java·Java
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        LocalDate lundi = LocalDate.of(2026, 10, 19);
        DateTimeFormatter format = DateTimeFormatter.ofPattern("dd/MM");
        String[] noms = {"lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"};

        for (int i = 0; i < 7; i++) {
            LocalDate jour = lundi.plusDays(i);
            String ligne = noms[i] + " " + jour.format(format);

            if (jour.getDayOfWeek() == DayOfWeek.SATURDAY || jour.getDayOfWeek() == DayOfWeek.SUNDAY) {
                ligne += " (week-end)";
            }
            System.out.println(ligne);
        }
    }
}
Console
lundi 19/10
mardi 20/10
mercredi 21/10
jeudi 22/10
vendredi 23/10
samedi 24/10 (week-end)
dimanche 25/10 (week-end)
Ma confianceNon évalué
✓ Vérifiable

Exercice 29.2.D La durée d'un trajet

Un train part le 19 octobre 2026 à 08h45 et arrive à 11h20. Calculez et affichez :

  1. la durée en minutes (ChronoUnit.MINUTES.between sur deux LocalDateTime) ;
  2. cette durée sous la forme 2 h 35 ;
  3. l'heure d'arrivée si le train a 25 minutes de retard (plusMinutes), au format HH:mm.
Console
Durée : 2 h 35
Arrivée avec retard : 11:45

Votre code à compléter

Main.java·Java
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : depart

        // Déclarez votre variable ici : arrivee

        // Déclarez votre variable ici : minutes

        // Affichez le résultat ici

        // Déclarez votre variable ici : avecRetard

        // Déclarez votre variable ici : heure

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class Main {
    public static void main(String[] args) {
        LocalDateTime depart = LocalDateTime.of(2026, 10, 19, 8, 45);
        LocalDateTime arrivee = LocalDateTime.of(2026, 10, 19, 11, 20);

        long minutes = ChronoUnit.MINUTES.between(depart, arrivee);
        System.out.println("Durée : " + (minutes / 60) + " h " + (minutes % 60));

        LocalDateTime avecRetard = arrivee.plusMinutes(25);
        DateTimeFormatter heure = DateTimeFormatter.ofPattern("HH:mm");
        System.out.println("Arrivée avec retard : " + avecRetard.format(heure));
    }
}
Console
Durée : 2 h 35
Arrivée avec retard : 11:45

Pour passer de 155 minutes à « 2 h 35 », on utilise la division entière (/ 60) et le reste (% 60), vus au chapitre 5.

Ma confianceNon évalué
✓ Vérifiable

Exercice 29.4.A Sauvegarder et recharger une liste

Écrivez la liste d'invités Léa, Hugo dans invites.txt. Relisez le fichier dans une nouvelle liste, ajoutez Inès, réécrivez le fichier, puis affichez son contenu relu.

Votre code à compléter

Main.java·Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException {
        // Déclarez votre variable ici : fichier

        // Créez votre liste ici : invites

        // Appelez la méthode write() ici

        // Créez votre liste ici : relus

        // Appelez la méthode add() ici

        // Appelez la méthode write() ici

        // Affichez le résultat ici
    }
}
Voir la solution

readAllLines peut rendre une liste qu'on ne peut pas modifier : on la copie dans une ArrayList avant d'ajouter (section 28.5).

Main.java·Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException {
        Path fichier = Path.of("invites.txt");

        List<String> invites = new ArrayList<>(List.of("Léa", "Hugo"));
        Files.write(fichier, invites);

        List<String> relus = new ArrayList<>(Files.readAllLines(fichier));
        relus.add("Inès");
        Files.write(fichier, relus);

        System.out.println(Files.readAllLines(fichier));
    }
}
Console
[Léa, Hugo, Inès]
Ma confianceNon évalué
✓ Vérifiable

Exercice 29.4.B Le journal de bord

Dans un fichier journal.txt :

  1. écrivez trois lignes : Départ, Escale à Lisbonne, Tempête ;
  2. ajoutez une quatrième ligne, Arrivée, à la fin du fichier, sans effacer les trois premières (option StandardOpenOption.APPEND de Files.writeString) ;
  3. relisez le fichier avec Files.readAllLines et affichez chaque ligne numérotée (1. Départ…), puis le nombre de lignes ;
  4. supprimez le fichier (Files.deleteIfExists).

Votre code à compléter

Main.java·Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException {
        // Déclarez votre variable ici : journal

        // Appelez la méthode writeString() ici

        // Complétez ici

        // Appelez la méthode writeString() ici

        // Créez votre liste ici : lignes

        // Écrivez votre boucle ici (for)

        // Affichez le résultat ici

        // Appelez la méthode deleteIfExists() ici
    }
}
Voir la solution
Main.java·Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException {
        Path journal = Path.of("journal.txt");

        Files.writeString(journal, "Départ\nEscale à Lisbonne\nTempête\n");

        // APPEND : on ajoute à la fin au lieu de remplacer le contenu
        Files.writeString(journal, "Arrivée\n", StandardOpenOption.APPEND);

        List<String> lignes = Files.readAllLines(journal);
        for (int i = 0; i < lignes.size(); i++) {
            System.out.println((i + 1) + ". " + lignes.get(i));
        }
        System.out.println(lignes.size() + " lignes");

        Files.deleteIfExists(journal);
    }
}
Console
1. Départ
2. Escale à Lisbonne
3. Tempête
4. Arrivée
4 lignes

Sans APPEND, le second writeString remplacerait tout le fichier par « Arrivée ». throws IOException est obligatoire sur main : toute lecture ou écriture de fichier peut échouer (exception vérifiée, section 22.11).

Ma confianceNon évalué
✓ Vérifiable

Exercice 29.4.C Le fichier introuvable

Lisez le fichier inexistant.txt avec Files.readAllLines, avec trois blocs :

  1. d'abord catch (NoSuchFileException e) : le fichier n'existe pas ; affichez Fichier introuvable : <nom> avec e.getMessage() ;
  2. puis catch (IOException e) : toute autre erreur de lecture ; affichez Lecture impossible. ;
  3. un finally qui affiche Fin de la lecture.

Pourquoi faut-il écrire NoSuchFileException avant IOException ?

Votre code à compléter

Main.java·Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Écrivez votre try / catch ici
    }
}
Voir la solution
Main.java·Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        try {
            List<String> lignes = Files.readAllLines(Path.of("inexistant.txt"));
            System.out.println(lignes.size() + " lignes lues");
        } catch (NoSuchFileException e) {
            System.out.println("Fichier introuvable : " + e.getMessage());
        } catch (IOException e) {
            System.out.println("Lecture impossible.");
        } finally {
            System.out.println("Fin de la lecture.");
        }
    }
}
Console
Fichier introuvable : inexistant.txt
Fin de la lecture.

NoSuchFileException hérite d'IOException : c'est le cas précis « le fichier n'existe pas ». Java lit les catch de haut en bas et prend le premier qui correspond : le cas précis doit venir avant le cas général, sinon il ne serait jamais atteint (le compilateur le refuserait).

Ma confianceNon évalué
✓ Vérifiable

Exercice 29.6.A Le total des ventes

Le fichier ventes.csv commence par une ligne d'en-tête, puis une vente par ligne :

ventes.csv
vendeur;montant
Léa;12.5
Hugo;8.0
Léa;4.5
Inès;20.0
Hugo;3.0

Affichez le total de chaque vendeur, par ordre alphabétique.

Votre code à compléter

Main.java·Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) throws IOException {
        // Créez votre liste ici : lignes

        // Créez votre dictionnaire (HashMap) ici : totaux

        // Parcourez avec une boucle for-each ici

        // Écrivez votre boucle ici (for)
    }
}
Voir la solution

Une TreeMap pour l'ordre alphabétique, getOrDefault pour additionner (chapitre 28), et subList(1, …) pour sauter l'en-tête :

Main.java·Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) throws IOException {
        List<String> lignes = Files.readAllLines(Path.of("ventes.csv"));
        Map<String, Double> totaux = new TreeMap<>();

        for (String ligne : lignes.subList(1, lignes.size())) { // on saute l'en-tête
            String[] champs = ligne.split(";");
            double montant = Double.parseDouble(champs[1]);
            totaux.put(champs[0], totaux.getOrDefault(champs[0], 0.0) + montant);
        }

        for (Map.Entry<String, Double> e : totaux.entrySet()) {
            System.out.println(e.getKey() + " : " + e.getValue() + " €");
        }
    }
}
Console
Hugo : 11.0 €
Inès : 20.0 €
Léa : 17.0 €
Ma confianceNon évalué
Non vérifiable

Exercice 29.6.B Les notes d'un fichier CSV

Un fichier notes.csv contient un en-tête et des lignes prénom;note : Léa;15, Hugo;12, Inès;18, Noé;abc, Zoé;9.

  1. Écrivez-le (avec Files.write(chemin, liste)), puis relisez-le.
  2. En ignorant l'en-tête, calculez la moyenne des notes valides et trouvez le meilleur élève.
  3. Une note qui n'est pas un nombre (Noé) est ignorée avec le message Ligne ignorée : Noé;abc.
  4. Affichez la moyenne et le meilleur élève, puis supprimez le fichier.

Votre code à compléter

Main.java·Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException {
        // Déclarez votre variable ici : fichier

        // Appelez la méthode write() ici

        // Créez votre liste ici : lignes

        // Déclarez votre variable ici : somme

        // Déclarez votre variable ici : nombre

        // Déclarez votre variable ici : meilleur

        // Déclarez votre variable ici : meilleureNote

        // Écrivez votre boucle ici (for)

        // Affichez le résultat ici

        // Appelez la méthode deleteIfExists() ici
    }
}
Voir la solution
Main.java·Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException {
        Path fichier = Path.of("notes.csv");
        Files.write(fichier, List.of("prenom;note", "Léa;15", "Hugo;12", "Inès;18", "Noé;abc", "Zoé;9"));

        List<String> lignes = Files.readAllLines(fichier);

        int somme = 0;
        int nombre = 0;
        String meilleur = "";
        int meilleureNote = -1;

        for (int i = 1; i < lignes.size(); i++) {          // i = 1 : on saute l'en-tête
            String[] morceaux = lignes.get(i).split(";");
            try {
                int note = Integer.parseInt(morceaux[1]);
                somme += note;
                nombre++;
                if (note > meilleureNote) {
                    meilleureNote = note;
                    meilleur = morceaux[0];
                }
            } catch (NumberFormatException e) {
                System.out.println("Ligne ignorée : " + lignes.get(i));
            }
        }

        System.out.println("Moyenne : " + ((double) somme / nombre));
        System.out.println("Meilleur élève : " + meilleur + " (" + meilleureNote + ")");

        Files.deleteIfExists(fichier);
    }
}
Console
Ligne ignorée : Noé;abc
Moyenne : 13.5
Meilleur élève : Inès (18)

Cet exercice réunit java.io (lire un fichier), java.util (List, split) et la gestion d'erreurs du chapitre 22 : une ligne mal formée ne doit pas faire planter tout le programme.

Ma confianceNon évalué
✓ Vérifiable

Testez-vous : QCM

QCM · Chapitre 29

  1. Quel type choisir pour l'heure d'ouverture d'une boutique ?

  2. Quel motif lit la date 14/07/2026 ?

  3. Comment obtenir le nombre de jours entre deux dates a et b ?

  4. Pourquoi Files.readString(chemin) ne compile-t-il pas seul dans un main sans throws ni try ?

  5. Que rend readLine() d'un BufferedReader à la fin du fichier ?

  6. Files.writeString(chemin, "Œufs\n") sur un fichier qui existe déjà…

  7. Comment ajouter une ligne à la fin d'un fichier sans effacer son contenu ?

  8. Dans un try qui lit un fichier, quel catch faut-il écrire en premier ?

1 gros exercice ou projet

Exercice 29.6.C Gros exercice : le journal des ventes (dates + fichier + CSV)

Exercice de synthèse du chapitre, qui réunit java.time, java.io et le découpage de texte. On tient un journal des ventes dans un fichier CSV, puis on l'analyse.

Chaque ligne du fichier a le format date;produit;montant, par exemple 2026-01-10;Baguette;1.10.

1. Écrire. Créez un fichier et écrivez-y quatre ventes (dates et montants au choix, sur plusieurs jours).

2. Lire en objets. Définissez record Vente(LocalDate date, String produit, double montant). Relisez le fichier et construisez une List<Vente> (pensez à LocalDate.parse(...) et Double.parseDouble(...)).

3. Analyser. Affichez : le total encaissé, et le nombre de jours entre la première et la dernière vente (avec ChronoUnit.DAYS.between).

Déroulé attendu :

Console
4 ventes, total : 23,20 €
De 2026-01-10 à 2026-01-15 (5 jours)

Votre code à compléter

Main.java·Java
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) throws Exception {
        // Complétez ici

        // Déclarez votre variable ici : f

        // Appelez la méthode writeString() ici

        // Complétez ici

        // Créez votre liste ici : ventes

        // Parcourez avec une boucle for-each ici

        // Complétez ici

        // Déclarez votre variable ici : total

        // Parcourez avec une boucle for-each ici

        // Déclarez votre variable ici : premiere

        // Calculez ici : derniere

        // Déclarez votre variable ici : jours

        // Affichez le résultat ici
    }
}
Voir la solution

L'exercice chaîne trois compétences : écrire/lire un fichier (Files), transformer chaque ligne en objet via split + parse, et calculer sur les dates avec ChronoUnit. Le record Vente porte une vraie LocalDate, pas un texte — d'où les calculs de dates faciles.

Main.java·Java
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

record Vente(LocalDate date, String produit, double montant) {}

public class Main {
    public static void main(String[] args) throws Exception {
        // 1. écrire le journal
        Path f = Path.of("ventes.csv");
        Files.writeString(f, """
            2026-01-10;Baguette;1.10
            2026-01-10;Croissant;1.20
            2026-01-12;Gâteau;18.50
            2026-01-15;Café;2.40
            """);

        // 2. relire en objets
        List<Vente> ventes = new ArrayList<>();
        for (String ligne : Files.readAllLines(f)) {
            if (ligne.isBlank()) continue;
            String[] c = ligne.split(";");
            ventes.add(new Vente(
                LocalDate.parse(c[0]),
                c[1],
                Double.parseDouble(c[2])
            ));
        }

        // 3. analyser
        double total = 0;
        for (Vente v : ventes) total += v.montant();

        LocalDate premiere = ventes.get(0).date();
        LocalDate derniere = ventes.get(ventes.size() - 1).date();
        long jours = ChronoUnit.DAYS.between(premiere, derniere);

        System.out.printf("%d ventes, total : %.2f €%n", ventes.size(), total);
        System.out.println("De " + premiere + " à " + derniere + " (" + jours + " jours)");

    }
}
Ma confianceNon évalué
✓ Vérifiable

Extras · 14 / 14

Le projet final

Chapitre 30

Chapitre 30 · Le projet final

Projet : la carte de fidélité de la boulangerie

Objectif. Construire, étape par étape, une vraie petite application qui réutilise tout ce qui a été vu dans la semaine.

Le cahier des charges

Au chapitre 12, la boulangerie avait une caisse. Elle veut maintenant fidéliser ses clients avec une carte de fidélité. Vous allez écrire l'application de la vendeuse : une application console, avec un menu.

Les règles du programme de fidélité :

  • chaque client reçoit un numéro de carte à l'inscription : C001, C002… ;
  • un achat rapporte 1 point par euro entier : 45,80 € rapportent 45 points ;
  • 100 points donnent une viennoiserie offerte : on retire 100 points ;
  • le niveau dépend des points cumulés depuis l'inscription (une récompense ne fait pas baisser le niveau) : BRONZE sous 200 points, ARGENT de 200 à 499, OR à partir de 500 ;
  • un client OR gagne le double de points. On prend le niveau au moment de l'achat.

Le menu de l'application :

1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
ÉtapeCe qu'on construitCe qu'on réutilise
1 · Le modèleAchat, Niveau, Clientclasses et encapsulation (13, 14), enum (24.2), record, equals et hashCode (22), switch moderne (25), LocalDate (27)
2 · Les règlesProgramme et deux exceptionsles maps (11, 26), les constantes (15), les exceptions vérifiées (19)
3 · Le menuCaisse et la boucle du MainScanner (6), les boucles (9), try / catch (19), switch moderne (25)
4 · Sauvegarder (facultatif)Sauvegarde et le fichier clients.csvles fichiers et IOException (27, 22.7), split (23)

Glossaire du chapitre

Cahier des charges
Description de ce que l'application doit faire.
Modèle
Les classes qui représentent les données du programme (client, carte…).
Règle métier
Règle propre à l'activité (par exemple 1 point par euro, niveaux de fidélité).
enum
Type à valeurs fixes, utile pour les niveaux de fidélité (BRONZE, ARGENT, OR).
Persistance
Sauvegarde des données dans un fichier pour les retrouver au prochain lancement.
Menu
Boucle qui propose des choix à l'utilisateur et appelle la bonne méthode.

4 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 30.2.A Le modèle

Instructions · Étape 1 · Le modèle

Trois types, chacun dans son fichier :

  • Achat : un record avec un montant (double) et une date (LocalDate). Un montant nul ou négatif est refusé avec une IllegalArgumentException, dès la création.
  • Niveau : un enum BRONZE, ARGENT, OR, avec une méthode static Niveau depuis(int cumul) et une méthode int multiplicateur() écrite avec un switch moderne.
  • Client : un numéro et un nom qui ne changent plus, des points disponibles et des points cumulés. Une méthode int acheter(Achat achat) ajoute les points et renvoie le nombre gagné ; retirerPoints(int nombre) les retire. Deux clients sont le même client s'ils ont le même numéro. toString affiche C001 · Léa · 45 points · BRONZE.

Écrivez Achat, Niveau et Client. Dans un Main :

  1. faites acheter Léa quatre fois (45,80 €, 180 €, 300 €, puis 10 €) et affichez les points gagnés à chaque achat : le dernier doit rapporter 20 points, car elle est passée OR ;
  2. vérifiez qu'un HashSet ne garde qu'un seul client quand on y ajoute deux objets Client avec le numéro C001 ;
  3. vérifiez qu'un achat de -3 € est refusé.
Voir la solution
Achat.java·Java
import java.time.LocalDate;

public record Achat(double montant, LocalDate date) {
    // Constructeur compact : on refuse un achat impossible dès sa création
    public Achat {
        if (montant <= 0) {
            throw new IllegalArgumentException("Montant invalide : " + montant);
        }
    }
}
Niveau.java·Java
public enum Niveau {
    BRONZE, ARGENT, OR;

    // Le niveau dépend des points cumulés depuis l'inscription
    public static Niveau depuis(int cumul) {
        if (cumul >= 500) {
            return OR;
        }
        if (cumul >= 200) {
            return ARGENT;
        }
        return BRONZE;
    }

    public int multiplicateur() {
        return switch (this) {
            case BRONZE, ARGENT -> 1;
            case OR -> 2;
        };
    }
}
Client.java·Java
import java.util.Objects;

public class Client {
    private final String numero;
    private final String nom;
    private int points;   // points disponibles
    private int cumul;    // points gagnés depuis l'inscription : fixe le niveau

    public Client(String numero, String nom, int points, int cumul) {
        this.numero = numero;
        this.nom = nom;
        this.points = points;
        this.cumul = cumul;
    }

    public Client(String numero, String nom) {
        this(numero, nom, 0, 0);
    }

    public int acheter(Achat achat) {
        // 1 point par euro entier, multiplié selon le niveau au moment de l'achat
        int gagnes = (int) achat.montant() * getNiveau().multiplicateur();
        points += gagnes;
        cumul += gagnes;
        return gagnes;
    }

    public void retirerPoints(int nombre) {
        points -= nombre;
    }

    public Niveau getNiveau() {
        return Niveau.depuis(cumul);
    }

    public String getNumero() { return numero; }
    public String getNom() { return nom; }
    public int getPoints() { return points; }
    public int getCumul() { return cumul; }

    // Deux clients sont le même client s'ils ont le même numéro
    @Override
    public boolean equals(Object o) {
        return o instanceof Client autre && numero.equals(autre.numero);
    }

    @Override
    public int hashCode() {
        return Objects.hash(numero);
    }

    @Override
    public String toString() {
        return String.format("%s · %s · %d points · %s", numero, nom, points, getNiveau());
    }
}
Main.java·Java
import java.time.LocalDate;
import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Client lea = new Client("C001", "Léa");
        System.out.println(lea);
        System.out.println("+" + lea.acheter(new Achat(45.80, LocalDate.of(2026, 10, 20))) + " points");
        System.out.println("+" + lea.acheter(new Achat(180, LocalDate.of(2026, 10, 21))) + " points");
        System.out.println(lea);
        System.out.println("+" + lea.acheter(new Achat(300, LocalDate.of(2026, 10, 22))) + " points");
        System.out.println(lea);
        System.out.println("+" + lea.acheter(new Achat(10, LocalDate.of(2026, 10, 23))) + " points (niveau OR)");

        Set<Client> clients = new HashSet<>();
        clients.add(lea);
        clients.add(new Client("C001", "Léa D."));   // même numéro : même client
        System.out.println(clients.size() + " client dans l'ensemble");

        System.out.println(new Achat(12.5, LocalDate.of(2026, 10, 20)));
        new Achat(-3, LocalDate.of(2026, 10, 20));
    }
}
Console
C001 · Léa · 0 points · BRONZE
+45 points
+180 points
C001 · Léa · 225 points · ARGENT
+300 points
C001 · Léa · 525 points · OR
+20 points (niveau OR)
1 client dans l'ensemble
Achat[montant=12.5, date=2026-10-20]
Exception in thread "main" java.lang.IllegalArgumentException: Montant invalide : -3.0
	at Achat.<init>(Achat.java:7)
	at Main.main(Main.java:22)

Le dernier achat plante volontairement : c'est le constructeur compact du record qui refuse le montant. Aux étapes suivantes, on rattrapera cette exception.

Ma confianceNon évalué
Non vérifiable

Exercice 30.3.A Les règles

Instructions · Étape 2 · Les règles

Une classe Programme garde tous les clients dans une map numéro → client, et applique les règles :

  • Client inscrire(String nom) crée le client avec le numéro suivant (C001, C002…) ;
  • Client trouver(String numero) lève une ClientInconnuException si le numéro n'existe pas ;
  • int acheter(String numero, double montant, LocalDate date) renvoie les points gagnés ;
  • int utiliserRecompense(String numero) retire 100 points et renvoie le reste, ou lève une PointsInsuffisantsException ;
  • List<Client> getClients() renvoie les clients dans l'ordre d'inscription.

Les deux exceptions sont vérifiées (elles héritent d'Exception) : ce sont des refus normaux, que l'appelant doit traiter (chapitre 22.8). Le prix d'une récompense est une constante.

Écrivez Programme, ClientInconnuException et PointsInsuffisantsException. Dans un Main sans saisie :

  1. inscrivez Léa et Hugo ;
  2. Léa achète pour 230,40 €, Hugo pour 12,50 € ;
  3. Léa prend une viennoiserie, puis Hugo essaie aussi ;
  4. essayez un achat sur la carte C099 ;
  5. affichez tous les clients.
Voir la solution
ClientInconnuException.java·Java
public class ClientInconnuException extends Exception {
    public ClientInconnuException(String numero) {
        super("client inconnu : " + numero);
    }
}
PointsInsuffisantsException.java·Java
public class PointsInsuffisantsException extends Exception {
    public PointsInsuffisantsException(int points) {
        super("points insuffisants (" + points + " sur " + Programme.PRIX_RECOMPENSE + ")");
    }
}
Programme.java·Java
import java.time.LocalDate;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class Programme {
    public static final int PRIX_RECOMPENSE = 100;

    // LinkedHashMap : les clients restent dans l'ordre d'inscription
    private final Map<String, Client> clients = new LinkedHashMap<>();

    public Client inscrire(String nom) {
        String numero = String.format("C%03d", clients.size() + 1);
        Client client = new Client(numero, nom);
        clients.put(numero, client);
        return client;
    }

    public Client trouver(String numero) throws ClientInconnuException {
        Client client = clients.get(numero);
        if (client == null) {
            throw new ClientInconnuException(numero);
        }
        return client;
    }

    public int acheter(String numero, double montant, LocalDate date) throws ClientInconnuException {
        return trouver(numero).acheter(new Achat(montant, date));
    }

    public int utiliserRecompense(String numero) throws ClientInconnuException, PointsInsuffisantsException {
        Client client = trouver(numero);
        if (client.getPoints() < PRIX_RECOMPENSE) {
            throw new PointsInsuffisantsException(client.getPoints());
        }
        client.retirerPoints(PRIX_RECOMPENSE);
        return client.getPoints();
    }

    public List<Client> getClients() {
        return List.copyOf(clients.values());   // une copie : personne ne modifie la map de l'extérieur
    }
}
Main.java·Java
import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        Programme programme = new Programme();
        LocalDate jour = LocalDate.of(2026, 10, 23);
        programme.inscrire("Léa");
        programme.inscrire("Hugo");

        try {
            System.out.println("Léa : +" + programme.acheter("C001", 230.40, jour) + " points");
            System.out.println("Hugo : +" + programme.acheter("C002", 12.50, jour) + " points");
            System.out.println("Léa, il reste " + programme.utiliserRecompense("C001") + " points");
            programme.utiliserRecompense("C002");
        } catch (ClientInconnuException | PointsInsuffisantsException e) {
            System.out.println("Refusé : " + e.getMessage());
        }

        try {
            programme.acheter("C099", 5, jour);
        } catch (ClientInconnuException e) {
            System.out.println("Refusé : " + e.getMessage());
        }

        for (Client client : programme.getClients()) {
            System.out.println(client);
        }
    }
}
Console
Léa : +230 points
Hugo : +12 points
Léa, il reste 130 points
Refusé : points insuffisants (12 sur 100)
Refusé : client inconnu : C099
C001 · Léa · 130 points · ARGENT
C002 · Hugo · 12 points · BRONZE

Une HashMap marcherait aussi, mais l'ordre de la liste des clients ne serait pas garanti ; la LinkedHashMap garde l'ordre d'inscription (chapitre 28). getClients() renvoie une copie : le code extérieur ne peut pas ajouter un client sans passer par inscrire.

Ma confianceNon évalué
Non vérifiable

Exercice 30.4.A Le menu

Instructions · Étape 3 · Le menu

Le Main affiche le menu en boucle jusqu'au choix 0. Chaque action demande ce qu'il lui faut (le nom, le numéro de carte, le montant) et affiche le résultat. Rangez les actions dans une classe Caisse, pour que le Main ne garde que la boucle.

L'application ne doit jamais planter devant une mauvaise saisie :

  • un choix inconnu affiche Choix inconnu : 9 ;
  • un montant qui n'est pas un nombre affiche un refus ; « 12,50 » est accepté comme « 12.50 » ;
  • un client inconnu, des points insuffisants ou un montant négatif affichent Refusé : … et le message de l'exception.

Écrivez Caisse et le Main. Testez avec la saisie ci-dessous :

  1. inscrire Léa ;
  2. un achat de 45,80 € ;
  3. un montant « trois euros » ;
  4. un achat pour la carte C002 qui n'existe pas ;
  5. une récompense trop tôt ;
  6. le choix 9 ;
  7. la liste des clients, puis quitter.
Voir la solution
Caisse.java·Java
import java.time.LocalDate;
import java.util.Scanner;

public class Caisse {
    private final Programme programme;
    private final Scanner scanner;

    public Caisse(Programme programme, Scanner scanner) {
        this.programme = programme;
        this.scanner = scanner;
    }

    // Toujours nextLine() : pas de piège du buffer (chapitre 6.6)
    private String demander(String question) {
        System.out.print(question);
        return scanner.nextLine().strip();
    }

    public void inscrire() {
        Client client = programme.inscrire(demander("Nom : "));
        System.out.println("Bienvenue " + client.getNom() + " ! Numéro de carte : " + client.getNumero());
    }

    public void acheter() throws ClientInconnuException {
        String numero = demander("Numéro de carte : ");
        // On accepte « 12,50 » comme « 12.50 »
        double montant = Double.parseDouble(demander("Montant : ").replace(',', '.'));
        System.out.println("+" + programme.acheter(numero, montant, LocalDate.now()) + " points");
    }

    public void recompense() throws ClientInconnuException, PointsInsuffisantsException {
        int reste = programme.utiliserRecompense(demander("Numéro de carte : "));
        System.out.println("Une viennoiserie offerte ! Il reste " + reste + " points.");
    }

    public void lister() {
        for (Client client : programme.getClients()) {
            System.out.println(client);
        }
    }
}
Main.java·Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Caisse caisse = new Caisse(new Programme(), scanner);
        boolean continuer = true;

        while (continuer) {
            System.out.println("1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter");
            System.out.print("Votre choix : ");
            String choix = scanner.nextLine().strip();
            try {
                switch (choix) {
                    case "1" -> caisse.inscrire();
                    case "2" -> caisse.acheter();
                    case "3" -> caisse.recompense();
                    case "4" -> caisse.lister();
                    case "0" -> continuer = false;
                    default -> System.out.println("Choix inconnu : " + choix);
                }
            } catch (NumberFormatException e) {   // avant IllegalArgumentException, sa classe mère
                System.out.println("Refusé : ce n'est pas un montant.");
            } catch (IllegalArgumentException | ClientInconnuException | PointsInsuffisantsException e) {
                System.out.println("Refusé : " + e.getMessage());
            }
        }
        System.out.println("À bientôt !");
    }
}

⌨ Valeurs à saisir, une par ligne

Console
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 1
Nom : Léa
Bienvenue Léa ! Numéro de carte : C001
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 2
Numéro de carte : C001
Montant : 45,80
+45 points
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 2
Numéro de carte : C001
Montant : trois euros
Refusé : ce n'est pas un montant.
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 2
Numéro de carte : C002
Montant : 10
Refusé : client inconnu : C002
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 3
Numéro de carte : C001
Refusé : points insuffisants (45 sur 100)
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 9
Choix inconnu : 9
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 4
C001 · Léa · 45 points · BRONZE
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 0
À bientôt !

L'ordre des catch compte : NumberFormatException hérite d'IllegalArgumentException, elle doit donc venir avant. Dans l'autre ordre, le compilateur refuse le programme, car le second catch ne pourrait jamais servir.

Ma confianceNon évalué
Non vérifiable

Exercice 30.5.A Sauvegarder (facultatif)

Instructions · Étape 4 · Sauvegarder (facultatif)

Pour l'instant, quand on quitte l'application, tous les clients sont perdus. Une classe Sauvegarde les écrit dans un fichier clients.csv, une ligne par client : numéro;nom;points;cumul.

  • enregistrer(List<Client> clients) écrit le fichier quand on quitte ;
  • charger() le relit au démarrage. Au tout premier lancement, le fichier n'existe pas encore : la liste est vide, sans erreur ;
  • Programme gagne une méthode reprendre(List<Client> anciens) qui remet ces clients dans la map ;
  • une IOException ne fait pas planter l'application : on affiche un message clair. À l'enregistrement, ce message doit dire que rien n'est enregistré.

Écrivez Sauvegarde, ajoutez reprendre à Programme, et chargez puis enregistrez dans le Main. Testez :

  1. première exécution : inscrivez Léa, un achat de 180 €, quittez ;
  2. ouvrez clients.csv ;
  3. relancez : Léa doit être toujours là.
Voir la solution
Sauvegarde.java·Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

public class Sauvegarde {
    private static final Path FICHIER = Path.of("clients.csv");

    // Une ligne par client : numéro;nom;points;cumul
    public static void enregistrer(List<Client> clients) throws IOException {
        List<String> lignes = new ArrayList<>();
        for (Client c : clients) {
            lignes.add(c.getNumero() + ";" + c.getNom() + ";" + c.getPoints() + ";" + c.getCumul());
        }
        Files.write(FICHIER, lignes);
    }

    public static List<Client> charger() throws IOException {
        List<Client> clients = new ArrayList<>();
        if (!Files.exists(FICHIER)) {
            return clients;   // premier lancement : pas encore de fichier
        }
        for (String ligne : Files.readAllLines(FICHIER)) {
            String[] champs = ligne.split(";");
            clients.add(new Client(champs[0], champs[1], Integer.parseInt(champs[2]), Integer.parseInt(champs[3])));
        }
        return clients;
    }
}
Programme.java·Java
import java.time.LocalDate;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class Programme {
    public static final int PRIX_RECOMPENSE = 100;

    // LinkedHashMap : les clients restent dans l'ordre d'inscription
    private final Map<String, Client> clients = new LinkedHashMap<>();

    public Client inscrire(String nom) {
        String numero = String.format("C%03d", clients.size() + 1);
        Client client = new Client(numero, nom);
        clients.put(numero, client);
        return client;
    }

    public Client trouver(String numero) throws ClientInconnuException {
        Client client = clients.get(numero);
        if (client == null) {
            throw new ClientInconnuException(numero);
        }
        return client;
    }

    public int acheter(String numero, double montant, LocalDate date) throws ClientInconnuException {
        return trouver(numero).acheter(new Achat(montant, date));
    }

    public int utiliserRecompense(String numero) throws ClientInconnuException, PointsInsuffisantsException {
        Client client = trouver(numero);
        if (client.getPoints() < PRIX_RECOMPENSE) {
            throw new PointsInsuffisantsException(client.getPoints());
        }
        client.retirerPoints(PRIX_RECOMPENSE);
        return client.getPoints();
    }

    // Au démarrage : on reprend les clients enregistrés la dernière fois
    public void reprendre(List<Client> anciens) {
        for (Client client : anciens) {
            clients.put(client.getNumero(), client);
        }
    }

    public List<Client> getClients() {
        return List.copyOf(clients.values());   // une copie : personne ne modifie la map de l'extérieur
    }
}
Main.java·Java
import java.io.IOException;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Programme programme = new Programme();
        try {
            programme.reprendre(Sauvegarde.charger());
            System.out.println(programme.getClients().size() + " client(s) chargé(s)");
        } catch (IOException e) {
            System.out.println("Impossible de lire clients.csv : " + e.getMessage());
        }
        Caisse caisse = new Caisse(programme, scanner);
        boolean continuer = true;

        while (continuer) {
            System.out.println("1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter");
            System.out.print("Votre choix : ");
            String choix = scanner.nextLine().strip();
            try {
                switch (choix) {
                    case "1" -> caisse.inscrire();
                    case "2" -> caisse.acheter();
                    case "3" -> caisse.recompense();
                    case "4" -> caisse.lister();
                    case "0" -> continuer = false;
                    default -> System.out.println("Choix inconnu : " + choix);
                }
            } catch (NumberFormatException e) {   // avant IllegalArgumentException, sa classe mère
                System.out.println("Refusé : ce n'est pas un montant.");
            } catch (IllegalArgumentException | ClientInconnuException | PointsInsuffisantsException e) {
                System.out.println("Refusé : " + e.getMessage());
            }
        }
        try {
            Sauvegarde.enregistrer(programme.getClients());
            System.out.println("Clients enregistrés. À bientôt !");
        } catch (IOException e) {
            System.out.println("ATTENTION, rien n'est enregistré : " + e.getMessage());
        }
    }
}

Premier lancement, sans fichier :

⌨ Valeurs à saisir, une par ligne

Console
0 client(s) chargé(s)
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 1
Nom : Léa
Bienvenue Léa ! Numéro de carte : C001
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 2
Numéro de carte : C001
Montant : 180
+180 points
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 0
Clients enregistrés. À bientôt !

Le fichier clients.csv contient alors une seule ligne : C001;Léa;180;180. Second lancement : Léa est reprise, et son achat de 30 € la fait passer ARGENT.

⌨ Valeurs à saisir, une par ligne

Console
1 client(s) chargé(s)
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 4
C001 · Léa · 180 points · BRONZE
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 2
Numéro de carte : C001
Montant : 30
+30 points
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 4
C001 · Léa · 210 points · ARGENT
1. Inscrire  2. Achat  3. Récompense  4. Clients  0. Quitter
Votre choix : 0
Clients enregistrés. À bientôt !
Ma confianceNon évalué
Non vérifiable

Testez-vous : QCM

QCM · Chapitre 30

  1. int[] a = {1, 2}; int[] b = a; b[0] = 9; Que vaut a[0] ?

  2. Une classe redéfinit equals mais pas hashCode. On ajoute à un HashSet deux objets égaux selon equals. Que donne size() ?

  3. Que ne fournit pas le record record Achat(double montant, LocalDate date) ?

  4. Dans Niveau, le switch (this) { case BRONZE, ARGENT -> 1; case OR -> 2; } n'a pas de default. Pourquoi compile-t-il ?

  5. sealed interface Recompense permits Viennoiserie, Remise {}. Que se passe-t-il si une classe Cafe écrit implements Recompense ?

  6. Pourquoi Programme range-t-il ses clients dans une LinkedHashMap plutôt qu'une HashMap ?

  7. LocalDate d = LocalDate.of(2026, 10, 23); d.plusDays(1); System.out.println(d); Qu'affiche ce code ?

  8. On appelle Files.readAllLines(fichier) dans une méthode, sans try / catch ni throws. Que se passe-t-il ?

Java Avancé · Partie 1 · 1 / 2

Introduction

Chapitre 31

Niveau

Java Avancé

Une formation distincte de Java Débutant : ses durées, ses exercices et son planning sont indépendants.

Chapitre 31 · Introduction

Bilan et programme du module

Objectif. Vérifier ce qui est acquis depuis le Java débutant, et voir comment ce module construit une application complète.

Les pom.xml du module, à copier-coller

Le fichier pom.xml de mediatheque change quatre fois. Voici les quatre versions complètes, précédées de celui du petit projet demo (chapitre 32) : à chaque fois, remplacez tout le contenu du fichier (à la racine du projet) puis rechargez Maven (bouton Load Maven Changes d'IntelliJ). Chaque version de mediatheque contient la précédente.

Projet demo (chapitre 32, étape 1 de la démo). Ne sert qu'aux exemples du cours.

pom.xml·Java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- Les trois coordonnées du projet : organisation, nom, version -->
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <!-- Version de Java utilisée pour compiler -->
        <maven.compiler.release>25</maven.compiler.release>
        <!-- Encodage des fichiers : évite les accents cassés -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

Version 1 · début du projet (chapitre 32). Aucune bibliothèque.

pom.xml·Java
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- Les coordonnées du projet : organisation, nom, version -->
    <groupId>formation</groupId>
    <artifactId>mediatheque</artifactId>
    <version>1.0</version>

    <properties>
        <!-- Version de Java et encodage des fichiers -->
        <maven.compiler.release>25</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

Version 2 · base de données (chapitre 35, étape 11). Ajoute H2 et son pilote JDBC.

pom.xml·Java
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- Les coordonnées du projet : organisation, nom, version -->
    <groupId>formation</groupId>
    <artifactId>mediatheque</artifactId>
    <version>1.0</version>

    <properties>
        <!-- Version de Java et encodage des fichiers -->
        <maven.compiler.release>25</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- H2 : la base de données et son pilote JDBC (le « driver » qui sait parler à H2) -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>2.3.232</version>
        </dependency>
    </dependencies>
</project>

Version 3 · journaux (chapitre 38, étape 22). Ajoute SLF4J.

pom.xml·Java
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- Les coordonnées du projet : organisation, nom, version -->
    <groupId>formation</groupId>
    <artifactId>mediatheque</artifactId>
    <version>1.0</version>

    <properties>
        <!-- Version de Java et encodage des fichiers -->
        <maven.compiler.release>25</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- H2 : la base de données et son pilote JDBC (le « driver » qui sait parler à H2) -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>2.3.232</version>
        </dependency>

        <!-- SLF4J : l'API de journalisation (ce que le code appelle) -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.16</version>
        </dependency>
        <!-- Une implémentation simple qui écrit dans la console (Spring Boot en apporte une autre) -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.16</version>
        </dependency>
    </dependencies>
</project>

Version 4 · tests (chapitre 39, étape 23). Ajoute JUnit, Mockito et le plugin qui lance mvn test. C'est la version finale.

pom.xml·Java
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- Les coordonnées du projet : organisation, nom, version -->
    <groupId>formation</groupId>
    <artifactId>mediatheque</artifactId>
    <version>1.0</version>

    <properties>
        <!-- Version de Java et encodage des fichiers -->
        <maven.compiler.release>25</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- H2 : la base de données et son pilote JDBC (le « driver » qui sait parler à H2) -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>2.3.232</version>
        </dependency>

        <!-- SLF4J : l'API de journalisation (ce que le code appelle) -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.16</version>
        </dependency>
        <!-- Une implémentation simple qui écrit dans la console (Spring Boot en apporte une autre) -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.16</version>
        </dependency>

        <!-- JUnit 5 : scope test = disponible uniquement pour les tests, pas dans l'application finale -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.11.3</version>
            <scope>test</scope>
        </dependency>

        <!-- Mockito : fabrique de faux objets pour les tests -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>5.14.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Le plugin qui lance les tests avec « mvn test » (une version récente comprend JUnit 5) -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.5.2</version>
            </plugin>
        </plugins>
    </build>
</project>

Glossaire du chapitre

Back-end
La partie du programme qui tourne côté serveur : elle reçoit des demandes, traite les données et stocke les informations dans une base.
Fil rouge
Le projet construit pas à pas pendant toute la formation : une petite médiathèque, qui sera le back-end de MédiaTech (le projet de la formation Angular).
QCM
Questions à choix multiples pour vérifier ce qui est acquis. Elles ne sont pas notées.
Spring
Framework Java qui assemble et fait tourner une application web. Il est vu dans une formation à part ; ce module en prépare les bases.
Démo
Partie où le formateur montre la notion sur le projet demo ; rien n'est conservé.
Projet Médiathèque
Partie où l'on applique la notion au projet mediatheque (le fil rouge).

Testez-vous : QCM

QCM · Chapitre 31

  1. Quel mot-clé rend un attribut accessible uniquement à l'intérieur de sa classe ?

  2. Quelle classe permet de stocker une liste qui peut grandir ou rétrécir ?

  3. Quelle boucle parcourt tous les éléments d'une liste sans gérer d'indice ?

  4. Quel bloc permet de rattraper une exception ?

  5. Quel mot-clé lève volontairement une exception ?

  6. Quel bloc ferme automatiquement une ressource, comme une connexion à la base ?

  7. Quel mot-clé permet à une classe d'implémenter une interface ?

À faire : pour chaque question, expliquez la réponse avec vos propres mots, à voix haute ou par écrit, avant d’ouvrir l’explication. Si vous n’arrivez pas à la formuler, relisez le chapitre avant de continuer.

Java Avancé · Partie 2 · 2 / 2

Projet fil rouge · Transition Spring

Chapitres 32 à 39

Chapitre 32 · Projet fil rouge · Transition Spring

Le projet Maven, le record et l'enum

Objectif. Créer un projet Maven, décrire une donnée avec un record et un enum, puis écrire le modèle Media de la médiathèque.

Glossaire du chapitre

Maven
Outil qui télécharge les bibliothèques dont un projet a besoin, et organise la compilation.
pom.xml
Fichier de configuration du projet : nom, version de Java, liste des dépendances.
record
Classe de données courte et immuable : ses attributs ne changent pas après la création.
Package
Dossier logique qui range les classes ; il correspond au nom du dossier sur le disque.
Enum
Liste fermée de valeurs possibles.

2 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 32.A Un record pour un chefEssentiel

Dans demo, créez un record Chef avec un nom (texte) et un nombre d'annees d'expérience (entier). Dans Main, créez un chef « Mamie Jeanne » avec 40 ans d'expérience et affichez-le.

Voir la solution
Main.java (extrait)·Java
Chef chef = new Chef("Mamie Jeanne", 40);   // new + constructeur généré
System.out.println(chef);                // toString() généré
Chef.java·Java
package com.example.demo;

// Un record déclare ses composants entre parenthèses : le constructeur et les accesseurs sont générés
public record Chef(String nom, int annees) {}
Console
Chef[nom=Mamie Jeanne, annees=40]
Ma confianceNon évalué

Exercice 32.B Une difficulté en enumEssentiel

Créez un enum Difficulte avec trois valeurs : FACILE, MOYEN, DIFFICILE. Ajoutez-la comme composant du record Recette (en dernière position), puis affichez pour chaque recette : « Crêpes : FACILE ».

Voir la solution
Difficulte.java·Java
package com.example.demo;

// Trois valeurs fixes, séparées par des virgules
public enum Difficulte { FACILE, MOYEN, DIFFICILE }
Main.java (extrait)·Java
Recette crepes = new Recette("Crêpes", Categorie.DESSERT, 30, true, Difficulte.FACILE);
// difficulte() est l'accesseur généré ; concaténer un enum affiche le nom de la constante
System.out.println(crepes.nom() + " : " + crepes.difficulte());
Recette.java·Java
public record Recette(String nom, Categorie categorie, int minutes, boolean vegetarienne, Difficulte difficulte) {}
Ma confianceNon évalué

Testez-vous : QCM

QCM · Chapitre 32

  1. Dans quel fichier déclare-t-on les dépendances d'un projet Maven ?

  2. Quel est le rôle de Maven dans le projet ?

  3. Quelles sont les trois coordonnées d'une dépendance ?

À faire : pour chaque question, expliquez la réponse avec vos propres mots, à voix haute ou par écrit, avant d’ouvrir l’explication. Si vous n’arrivez pas à la formuler, relisez le chapitre avant de continuer.

Chapitre 33 · Projet fil rouge · Transition Spring

Génériques, classes anonymes et lambdas

Objectif. Savoir lire List<Media>, comprendre qu'une lambda est une classe anonyme abrégée, et trier une liste avec un Comparator.

Glossaire du chapitre

Générique
Classe ou méthode qui travaille avec un type donné plus tard, écrit entre < et >.
Paramètre de type
Le nom du type à remplir (T, A, B). Il est remplacé par un vrai type, par exemple String, quand on utilise la classe.
Classe anonyme
Classe sans nom, écrite sur place avec new Interface() { ... } ou new Classe() { ... }.
Lambda
Fonction écrite sans nom, en une ligne : (a, b) -> a - b.
Interface fonctionnelle
Interface qui ne contient qu'une seule méthode abstraite. Une lambda peut l'implémenter.
Référence de méthode
Raccourci pour une lambda qui appelle une méthode existante : String::length.
Capture
Une variable extérieure utilisée dans une lambda. Elle doit ne jamais changer après sa création.

2 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 33.A Le plus grand d'une listeEssentiel

Écrivez une méthode générique maximum qui reçoit une List<T> (avec T extends Comparable<T>) et renvoie le plus grand élément. Testez avec List.of(4, 9, 2).

Console
9

Votre code à compléter

Main.java·Java
import java.util.List;

public class Main {
    // Méthode à écrire : static <T extends Comparable<T>> T maximum(List<T> liste)

    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.util.List;

public class Main {
    static <T extends Comparable<T>> T maximum(List<T> liste) {
        T meilleur = liste.get(0);   // on commence par le premier
        for (T e : liste) {
            if (e.compareTo(meilleur) > 0) {
                meilleur = e;         // e est plus grand : on le garde
            }
        }
        return meilleur;
    }

    public static void main(String[] args) {
        System.out.println(maximum(List.of(4, 9, 2)));
    }
}
Ma confianceNon évalué

Exercice 33.B Un test de positivitéEssentiel

Créez un Predicate<Integer> nommé estPositif. Testez-le avec -3 puis avec 5.

Console
false
true

Votre code à compléter

Main.java·Java
import java.util.function.Predicate;

public class Main {
    public static void main(String[] args) {
        // Déclarez votre variable ici : estPositif

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.util.function.Predicate;

public class Main {
    public static void main(String[] args) {
        Predicate<Integer> estPositif = n -> n >= 0;
        System.out.println(estPositif.test(-3));
        System.out.println(estPositif.test(5));
    }
}
Ma confianceNon évalué

Testez-vous : QCM

QCM · Chapitre 33

  1. Que signifie T dans Boite<T> ?

  2. Pourquoi écrit-on List<Integer> et non List<int> ?

  3. Quelle écriture déclare une classe générique ?

  4. Quelle interface utiliser pour une fonction qui reçoit un nombre et renvoie un texte ?

  5. Qu'est-ce que x -> x * 2 en Java ?

À faire : pour chaque question, expliquez la réponse avec vos propres mots, à voix haute ou par écrit, avant d’ouvrir l’explication. Si vous n’arrivez pas à la formuler, relisez le chapitre avant de continuer.

Chapitre 34 · Projet fil rouge · Transition Spring

Stream et Optional, puis le dépôt en mémoire

Objectif. Lire et écrire des Stream et des Optional, puis écrire le dépôt en mémoire de la médiathèque.

Les 12 médias de départ, à copier-coller

À l'étape 10, créez cette classe dans com.example.mediatheque. Elle contient les 12 médias du catalogue : 4 livres, 4 films et 4 jeux. Trois sont déjà empruntés au départ (L'Étranger, Les Sept Samouraïs et Portal 2) : c'est le même catalogue que celui du front Angular. Deux médias n'ont pas de note (null).

mediatheque/src/main/java/com/example/mediatheque/DonneesInitiales.java·Java
package com.example.mediatheque;

import com.example.mediatheque.dao.MediaDao;
import com.example.mediatheque.model.MediaType;
import com.example.mediatheque.model.NewMedia;
import java.util.List;

// Charge les médias de départ (le catalogue de la médiathèque) si la base est vide
public class DonneesInitiales {

    public static void charger(MediaDao dao) {
        // Base déjà remplie : on ne recharge pas
        if (!dao.lister().isEmpty()) {
            return;
        }
        // List.of(...) : liste immuable écrite en une ligne
        List<NewMedia> medias = List.of(
            new NewMedia("Dune", "Frank Herbert", 1965, MediaType.LIVRE, "Science-fiction", 4.7, 21),
            new NewMedia("Le Petit Prince", "Antoine de Saint-Exupéry", 1943, MediaType.LIVRE, "Conte", 4.8, 21),
            new NewMedia("L'Étranger", "Albert Camus", 1942, MediaType.LIVRE, "Roman", 4.2, 21),
            new NewMedia("Fondation", "Isaac Asimov", 1951, MediaType.LIVRE, "Science-fiction", null, 21),
            new NewMedia("Blade Runner", "Ridley Scott", 1982, MediaType.FILM, "Science-fiction", 4.5, 7),
            new NewMedia("Le Voyage de Chihiro", "Hayao Miyazaki", 2001, MediaType.FILM, "Animation", 4.9, 7),
            new NewMedia("Les Sept Samouraïs", "Akira Kurosawa", 1954, MediaType.FILM, "Aventure", 4.8, 7),
            new NewMedia("Le Fabuleux Destin d'Amélie Poulain", "Jean-Pierre Jeunet", 2001, MediaType.FILM, "Comédie", 4.3, 7),
            new NewMedia("Hollow Knight", "Team Cherry", 2017, MediaType.JEU, "Aventure", 4.6, 14),
            new NewMedia("Celeste", "Maddy Makes Games", 2018, MediaType.JEU, "Plateforme", 4.4, 14),
            new NewMedia("Portal 2", "Valve", 2011, MediaType.JEU, "Puzzle", 4.7, 14),
            new NewMedia("Stardew Valley", "ConcernedApe", 2016, MediaType.JEU, "Simulation", null, 14)
        );
        // Les ids sont attribués dans l'ordre : 1, 2, 3…
        for (NewMedia m : medias) {
            dao.ajouter(m);
        }
        // Ces trois médias sont déjà empruntés au départ
        for (int id : List.of(3, 7, 11)) {
            dao.changerDisponibilite(id, false);
        }
    }
}

Glossaire du chapitre

Stream
Un flux d'éléments qu'on traite étape par étape, sans écrire de boucle.
Opération intermédiaire
Étape qui renvoie un nouveau Stream : filter, map, sorted.
Opération terminale
Étape qui termine le flux et renvoie un résultat : sum, count, collect.
Paresseux
Un Stream ne fait rien tant qu'il n'y a pas d'opération terminale.
Collectors
Outils qui rassemblent le résultat d'un Stream, par exemple dans une liste ou un regroupement.
null
Absence de valeur. Utiliser une variable null provoque une NullPointerException.
Optional
Une boîte qui contient une valeur, ou rien. Elle rend l'absence visible dans le code.
orElse
Donne une valeur de secours quand l'Optional est vide.
Dépôt (repository)
Classe qui range et retrouve des objets. Ici, elle gère les médias.
Contrat
Interface qui décrit les méthodes disponibles, sans dire comment elles sont faites.

4 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 34.A Des prénoms en majuscules, triésEssentiel

Partez de List.of("sami", "ali", "léa"). Mettez chaque prénom en majuscules, triez-les, puis affichez la liste.

Console
[ALI, LÉA, SAMI]

Votre code à compléter

Main.java·Java
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Créez votre liste ici : prenoms

        // Complétez ici

        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> prenoms = List.of("sami", "ali", "léa");
        List<String> resultat = prenoms.stream()
                .map(String::toUpperCase)
                .sorted()
                .toList();
        System.out.println(resultat);
    }
}
Ma confianceNon évalué

Exercice 34.B Le premier nombre pairEssentiel

Écrivez une méthode premierPair(List<Integer> liste) qui renvoie un Optional<Integer> contenant le premier nombre pair, ou vide s'il n'y en a pas. Après filter, utilisez .findFirst() : il renvoie la première valeur du flux dans un Optional, et un Optional vide si aucun élément ne correspond. Testez avec List.of(1, 3, 4, 6) puis List.of(1, 3).

Console
Optional[4]
Optional.empty

Votre code à compléter

Main.java·Java
import java.util.List;
import java.util.Optional;

public class Main {
    // Méthode à écrire : static Optional<Integer> premierPair(List<Integer> liste)

    public static void main(String[] args) {
        // Affichez le résultat ici
    }
}
Voir la solution
Main.java·Java
import java.util.List;
import java.util.Optional;

public class Main {
    static Optional<Integer> premierPair(List<Integer> liste) {
        return liste.stream()
                .filter(n -> n % 2 == 0)
                .findFirst();   // findFirst : première valeur du flux, dans un Optional (vide si rien ne correspond)
    }

    public static void main(String[] args) {
        System.out.println(premierPair(List.of(1, 3, 4, 6)));
        System.out.println(premierPair(List.of(1, 3)));
    }
}
Ma confianceNon évalué

Exercice 34.C Compter les médias disponiblesEssentiel

Ajoutez deux médias supplémentaires dont un avec available = false. Affichez le nombre de médias disponibles avec un Stream.

Voir la solution
Main.java·Java
long dispo = dao.lister().stream().filter(Media::available).count();
System.out.println(dispo);
Console
2
Ma confianceNon évalué

Exercice 34.D Rechercher par auteur dans le dépôtEssentiel

Ajoutez List<Media> trouverParAuteur(String author) à l'interface MediaDao, puis implémentez-la dans MediaDaoMemoire. Testez avec « Voltaire ».

Votre code à compléter

dao/MediaDao.java·Java

import com.example.mediatheque.model.Media;
import java.util.List;
import java.util.Optional;

public interface MediaDao {
    // Méthode abstraite à déclarer : void ajouter(Media media)
    // Méthode abstraite à déclarer : List<Media> lister()
    // Méthode abstraite à déclarer : Optional<Media> trouverParTitre(String title)
    // Méthode abstraite à déclarer : List<Media> trouverParAuteur(String author)
}
dao/MediaDaoMemoire.java·Java

import com.example.mediatheque.model.Media;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class MediaDaoMemoire implements MediaDao {
    // Attributs : liste
    // Méthode à écrire : public void ajouter(Media media)
    // Méthode à écrire : public List<Media> lister()
    // Méthode à écrire : public Optional<Media> trouverParTitre(String title)
    // Méthode à écrire : public List<Media> trouverParAuteur(String author)
}
Voir la solution
Ce qui change dans ce fichier
  • Exercice B : ajout de trouverParAuteur dans l'interface MediaDao.

Pourquoi : Une méthode ajoutée au contrat doit être implémentée partout : c'est pour cela que l'interface change en premier.

dao/MediaDao.java·Java
package com.example.mediatheque.dao;

import com.example.mediatheque.model.Media;
import java.util.List;
import java.util.Optional;

public interface MediaDao {
    void ajouter(Media media);
    List<Media> lister();
    Optional<Media> trouverParTitre(String title);
    List<Media> trouverParAuteur(String author);
}
Ce qui change dans ce fichier
  • Exercice B : implémentation de trouverParAuteur avec un filter sur author, puis toList().

Pourquoi : Sans cette méthode, la classe ne compile plus : elle doit tenir la promesse de l'interface.

dao/MediaDaoMemoire.java·Java
package com.example.mediatheque.dao;

import com.example.mediatheque.model.Media;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class MediaDaoMemoire implements MediaDao {
    private final List<Media> liste = new ArrayList<>();

    @Override
    public void ajouter(Media media) {
        liste.add(media);
    }

    @Override
    public List<Media> lister() {
        return List.copyOf(liste);
    }

    @Override
    public Optional<Media> trouverParTitre(String title) {
        return liste.stream().filter(l -> l.title().equals(title)).findFirst();
    }

    @Override
    public List<Media> trouverParAuteur(String author) {
        return liste.stream().filter(l -> l.author().equals(author)).toList();
    }
}

Dans Main : System.out.println(dao.trouverParAuteur("Voltaire").size()); affiche 2 avec les trois médias de l'exercice A.

Ma confianceNon évalué

Testez-vous : QCM

QCM · Chapitre 34

  1. Que fait filter dans un Stream ?

  2. Quelle opération termine un Stream et renvoie un résultat ?

  3. Que fait orElse("Inconnu") sur un Optional vide ?

  4. Quel est le problème principal de null ?

À faire : pour chaque question, expliquez la réponse avec vos propres mots, à voix haute ou par écrit, avant d’ouvrir l’explication. Si vous n’arrivez pas à la formuler, relisez le chapitre avant de continuer.

Chapitre 35 · Projet fil rouge · Transition Spring

H2 et JDBC : la connexion et la table

Objectif. Ajouter une base H2 à un projet Maven, s'y connecter en JDBC et créer une table.

Pourquoi une base de données, alors que la liste en mémoire fonctionnait ?

Pourquoi on a commencé en mémoire. Une simple liste ne demande rien à installer : on a pu se concentrer sur Stream, Optional et sur le contrat MediaDao, avant même de savoir où les médias seraient rangés.

Ses limites. Les médias vivent dans la mémoire du programme : tout disparaît dès qu'on l'arrête (à chaque lancement, on a dû recharger les 12 médias). Aucun autre programme ne peut les lire, et il n'y a ni contrainte (champ obligatoire, identifiant unique) ni requête : pour chercher, on parcourt toute la liste.

Ce que la base apporte. Les données survivent à l'arrêt du programme ; la base attribue elle-même les identifiants et refuse les données invalides ; on interroge avec du SQL ; et plus tard le front, le service et les tests pourront partager les mêmes données. Un emprunt pourra aussi être enregistré en une seule opération (chapitre 37).

On ne recommence pas pour autant. Le contrat MediaDao ne change pas : on ajoute seulement une seconde classe qui le respecte, MediaDaoJdbc. MediaDaoMemoire est gardé : il servira aux tests (chapitre 39).

La console H2 et le script SQL, à copier-coller

1. Lancer la console H2 : dans un terminal, une fois Maven rechargé à l'étape 11 (le jar de H2 se trouve alors dans votre dépôt Maven local, ~/.m2) :

Terminal·Terminal
java -cp ~/.m2/repository/com/h2database/h2/2.3.232/h2-2.3.232.jar org.h2.tools.Console

Sous Windows PowerShell, remplacez ~ par $HOME. Un navigateur s'ouvre sur http://localhost:8082.

2. Se connecter à la base du projet :

ChampValeur à saisir
Saved SettingsGeneric H2 (Embedded)
JDBC URLjdbc:h2:file:/chemin/absolu/vers/mediatheque (sans l'extension .mv.db)
User Namevide
Passwordvide

Un seul programme à la fois sur le fichier. La console H2 et votre programme Java ne peuvent pas ouvrir la base en même temps. Avant de lancer un programme Java, cliquez sur Disconnect (icône rouge en haut à gauche de la console) ou arrêtez la console avec Ctrl + C ; avant d'ouvrir la console, arrêtez le programme Java (carré rouge d'IntelliJ). Sinon : « Database may be already in use » ou « The file is locked ».

3. Créer la table : collez ce script (étape 12), puis lancez-le avec Ctrl + Entrée. La table est créée si elle n'existe pas, puis la requête SELECT affiche son contenu (vide au départ) :

Script SQL (console H2)·SQL
CREATE TABLE IF NOT EXISTS media (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    author VARCHAR(255) NOT NULL,
    release_year INT NOT NULL,
    type VARCHAR(10) NOT NULL,
    genre VARCHAR(100) NOT NULL,
    available BOOLEAN NOT NULL,
    rating DOUBLE,
    loan_days INT NOT NULL
);

SELECT * FROM media;

4. Voir les médias : à tout moment, cette requête affiche le contenu de la table :

Requête (console H2)·SQL
SELECT * FROM media;

Glossaire du chapitre

Schéma
La structure de la table : ses colonnes et leur type.
Clé primaire
Colonne qui identifie chaque ligne de façon unique.
JDBC
Java DataBase Connectivity : la bibliothèque standard de Java pour parler à une base de données (package java.sql).
Pilote (driver)
Classe fournie par un moteur de base (ici H2) qui sait traduire les appels JDBC pour ce moteur.
URL JDBC
L'adresse de la base, qui commence par jdbc: et indique le moteur, par exemple jdbc:h2:file:./mediatheque.
Connection
Une connexion ouverte vers la base. Elle doit être fermée à la fin (try-with-resources).

Chapitre 36 · Projet fil rouge · Transition Spring

JDBC : ajouter, lire et chercher

Objectif. Écrire un dépôt JDBC qui ajoute, liste et cherche des médias, avec des requêtes préparées.

Glossaire du chapitre

ResultSet
Le résultat d'un SELECT. On le parcourt ligne par ligne avec next().
PreparedStatement
Requête préparée : le SQL contient des ? et les valeurs sont envoyées séparément.
Injection SQL
Attaque où une saisie modifie la requête SQL envoyée à la base.
DAO
Data Access Object : objet dont le seul rôle est d'accéder aux données (lire, ajouter, supprimer). Le reste du programme ne voit pas le SQL.

2 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 36.A Compter les médiasEssentiel

Dans MediaDaoJdbc, ajoutez une méthode compter() qui renvoie le nombre de médias (un int), avec SELECT COUNT(*) FROM media. Affichez le résultat dans EssaiJdbc.

Indice : le résultat tient en une seule ligne et une seule colonne : rs.next() puis rs.getInt(1) (colonne numéro 1).

Voir la solution
dao/MediaDaoJdbc.java (extrait)·Java
public int compter() {
    // COUNT(*) renvoie une seule ligne avec une seule colonne : le nombre de lignes de la table
    String sql = "SELECT COUNT(*) FROM media";
    try (Connection c = connexion();
         PreparedStatement ps = c.prepareStatement(sql);
         ResultSet rs = ps.executeQuery()) {
        rs.next();               // on se place sur l'unique ligne
        return rs.getInt(1);     // première (et seule) colonne
    } catch (SQLException e) {
        throw new RuntimeException("Comptage impossible", e);
    }
}
Le code
  1. String sql = "SELECT COUNT(*) FROM media";COUNT(*) renvoie une seule ligne avec une seule colonne : le nombre de lignes de la table.
  2. rs.next();On se place sur l'unique ligne.
  3. return rs.getInt(1);Première (et seule) colonne.
Ma confianceNon évalué

Exercice 36.B Chercher par genreEssentiel

Ajoutez parGenre(String genre) qui renvoie la liste des médias de ce genre, sans tenir compte des majuscules (« roman » trouve « Roman »). Contrairement à chercher, le genre doit être exactement celui demandé (pas de LIKE).

Indice : comme trouverParTitre, avec LOWER(genre) = LOWER(?), mais on lit toutes les lignes avec while (rs.next()).

Voir la solution
dao/MediaDaoJdbc.java (extrait)·Java
public List<Media> parGenre(String genre) {
    String sql = "SELECT * FROM media WHERE LOWER(genre) = LOWER(?) ORDER BY id";
    List<Media> medias = new ArrayList<>();
    try (Connection c = connexion();
         PreparedStatement ps = c.prepareStatement(sql)) {
        ps.setString(1, genre);
        try (ResultSet rs = ps.executeQuery()) {
            while (rs.next()) {
                medias.add(lire(rs));
            }
        }
        return medias;
    } catch (SQLException e) {
        throw new RuntimeException("Recherche impossible", e);
    }
}
Ma confianceNon évalué

Testez-vous : QCM

QCM · Chapitre 36

  1. Que représente le ? dans une requête préparée ?

  2. Pourquoi PreparedStatement protège-t-il contre l'injection ?

  3. Quel est le risque principal d'une requête construite par concaténation ?

  4. Quel est le numéro du premier ? dans un PreparedStatement ?

À faire : pour chaque question, expliquez la réponse avec vos propres mots, à voix haute ou par écrit, avant d’ouvrir l’explication. Si vous n’arrivez pas à la formuler, relisez le chapitre avant de continuer.

Chapitre 37 · Projet fil rouge · Transition Spring

JDBC : modifier, supprimer et emprunter

Objectif. Modifier et supprimer des lignes, puis regrouper plusieurs requêtes dans une transaction, pour enregistrer un emprunt.

Les tables des emprunts, à copier-coller

À l'étape 18, ouvrez la console H2 (voir le chapitre 35) et exécutez ce script avec Ctrl + Entrée. Il crée les tables loan et loan_media si elles n'existent pas. Fermez la console avant de lancer un programme Java : un seul programme à la fois sur le fichier.

Script SQL (console H2)·Java
CREATE TABLE IF NOT EXISTS loan (
    id INT AUTO_INCREMENT PRIMARY KEY,
    loan_date DATE NOT NULL,
    return_date DATE NOT NULL
);
-- Table de liaison : un emprunt contient plusieurs médias
CREATE TABLE IF NOT EXISTS loan_media (
    loan_id INT NOT NULL,
    media_id INT NOT NULL,
    PRIMARY KEY (loan_id, media_id),
    FOREIGN KEY (loan_id) REFERENCES loan(id),
    FOREIGN KEY (media_id) REFERENCES media(id)
);

Glossaire du chapitre

Transaction
Groupe de modifications qui réussissent toutes ensemble (commit) ou sont toutes annulées (rollback).
Clé étrangère
Colonne qui doit désigner une ligne existante d'une autre table.
Table de liaison
Table qui relie deux autres tables (ici un emprunt et ses médias).
Rollback
Annulation de tout ce qu'une transaction a fait depuis son début.

2 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 37.A Changer la durée de prêtEssentiel

Dans MediaDaoJdbc, ajoutez changerDuree(int id, int jours) : elle modifie la colonne loan_days et renvoie true si le média existe. Dans EssaiJdbc, passez Dune (id 1) à 14 jours, puis appelez la méthode pour l'id 999 : que renvoie-t-elle ?

Voir la solution
dao/MediaDaoJdbc.java (extrait)·Java
public boolean changerDuree(int id, int jours) {
    String sql = "UPDATE media SET loan_days = ? WHERE id = ?";
    try (Connection c = connexion();
         PreparedStatement ps = c.prepareStatement(sql)) {
        ps.setInt(1, jours);
        ps.setInt(2, id);
        return ps.executeUpdate() > 0;     // nombre de lignes modifiées : 0 = aucun média avec cet id
    } catch (SQLException e) {
        throw new RuntimeException("Modification impossible", e);
    }
}
Console
true    // changerDuree(1, 14) : le média 1 existe
false   // changerDuree(999, 14) : aucune ligne modifiée
Ma confianceNon évalué

Exercice 37.B Supprimer par titreEssentiel

Ajoutez supprimerParTitre(String titre) qui supprime les médias portant ce titre (sans tenir compte des majuscules) et renvoie combien de lignes ont été supprimées. Que renvoie un second appel avec le même titre ?

Voir la solution
dao/MediaDaoJdbc.java (extrait)·Java
public int supprimerParTitre(String titre) {
    String sql = "DELETE FROM media WHERE LOWER(title) = LOWER(?)";
    try (Connection c = connexion();
         PreparedStatement ps = c.prepareStatement(sql)) {
        ps.setString(1, titre);
        return ps.executeUpdate();         // le nombre de lignes supprimées
    } catch (SQLException e) {
        throw new RuntimeException("Suppression impossible", e);
    }
}
Console
1   // premier appel : le média existait
0   // second appel : déjà supprimé
Ma confianceNon évalué

Testez-vous : QCM

QCM · Chapitre 37

  1. Que fait rollback() ?

  2. Qu'apporte une transaction ?

  3. Que signifie DAO ?

  4. Quel est l'intérêt principal d'une interface DAO ?

À faire : pour chaque question, expliquez la réponse avec vos propres mots, à voix haute ou par écrit, avant d’ouvrir l’explication. Si vous n’arrivez pas à la formuler, relisez le chapitre avant de continuer.

Chapitre 38 · Projet fil rouge · Transition Spring

Couches, injection de dépendances et logs

Objectif. Séparer le code en couches (service, dépôt), donner au service ses dépendances par le constructeur et journaliser, comme le fera Spring.

Glossaire du chapitre

Interface
Contrat qui décrit ce qu'on sait faire, sans dire comment.
Implémentation
Classe qui fait réellement le travail en respectant une interface (par exemple MediaDaoJdbc).
Couplage
Dépendance entre deux classes. Moins il y en a, plus le code est facile à changer.
Service
Couche qui applique les règles métier, entre le contrôleur et le DAO.
Règle métier
Règle propre à l'activité, par exemple : un titre ne peut pas être vide.
Couche
Partie du programme avec une responsabilité précise. Chaque couche parle surtout à la suivante.
Injection de dépendances
Donner à un objet ce dont il a besoin (par son constructeur) au lieu de le laisser le créer.
Journal (log)
Message écrit pendant l'exécution avec un niveau : info, warn, error.
SLF4J
Bibliothèque de journalisation utilisée par Spring Boot.

2 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 38.A Une durée de prêt maximaleEssentiel

Ajoutez dans MediaService.ajouter une règle : un média dont la durée de prêt dépasse 90 jours est refusé (IllegalArgumentException, message « La durée de prêt ne peut pas dépasser 90 jours »). Placez-la avec les autres règles, avant l'appel au dépôt.

Voir la solution
MediaService.java (extrait)·Java
        // Règle métier : pas plus de 90 jours de prêt
        if (n.loanDays() > 90) {
            throw new IllegalArgumentException("La durée de prêt ne peut pas dépasser 90 jours");
        }
Ma confianceNon évalué

Exercice 38.B Les médias disponibles, par le serviceEssentiel

Ajoutez à MediaService une méthode disponibles() qui renvoie uniquement les médias disponibles, en passant par medias.lister() (le service ne touche pas à la liste interne du dépôt).

Voir la solution
MediaService.java (extrait)·Java
    // Le service combine les données du dépôt : le dépôt, lui, n'a pas changé
    public List<Media> disponibles() {
        return medias.lister().stream()          // stream : parcours de la liste
                .filter(Media::available)        // on garde les médias disponibles
                .toList();                       // on reforme une liste
    }
Le code
  1. public List<Media> disponibles() {Le service combine les données du dépôt : le dépôt, lui, n'a pas changé.
  2. return medias.lister().stream()Stream : parcours de la liste.
  3. .filter(Media::available)On garde les médias disponibles.
  4. .toList();On reforme une liste.
Ma confianceNon évalué

Testez-vous : QCM

QCM · Chapitre 38

  1. Quel code HTTP Spring renverra-t-il, plus tard, pour l'ajout d'un titre déjà existant ?

  2. Dans new MediaService(medias, loans), que fait-on ?

  3. Quel est l'intérêt d'avoir le champ du service de type MediaDao (l'interface) plutôt que MediaDaoJdbc ?

  4. Où doivent se trouver les règles métier (« titre obligatoire », quota de 5 emprunts) ?

  5. Quelle exception correspond à un titre déjà existant, selon notre convention ?

  6. Pourquoi écrire log.info("Ajout de {}", titre) plutôt que log.info("Ajout de " + titre) ?

  7. Quel est le rôle d'une interface DAO ?

  8. Pourquoi le reste du programme ne doit-il pas connaître JDBC ?

À faire : pour chaque question, expliquez la réponse avec vos propres mots, à voix haute ou par écrit, avant d’ouvrir l’explication. Si vous n’arrivez pas à la formuler, relisez le chapitre avant de continuer.

Chapitre 39 · Projet fil rouge · Transition Spring

Tester le code : JUnit et faux dépôts

Objectif. Écrire des tests JUnit 5 qui vérifient les règles du service, avec un dépôt en mémoire, puis une base H2 en mémoire, et reconnaître Mockito.

Glossaire du chapitre

Test unitaire
Test qui vérifie une petite partie du code (une méthode, une classe), isolément.
Assertion
Vérification qui échoue si la valeur obtenue n'est pas celle attendue.
JUnit 5
Bibliothèque de tests la plus utilisée en Java.
Exception attendue
Test qui réussit si le code lève bien l'erreur prévue.
Faux dépôt (mock)
Objet qui remplace un vrai collaborateur dans un test et dont on contrôle les réponses.
Mockito
Bibliothèque qui fabrique des faux objets en une ligne (aperçu).
Test d'intégration
Test qui fait travailler plusieurs vrais composants ensemble (par exemple le dépôt et la base).

2 exercices

Réalisez chaque exercice dans IntelliJ. Complétez le code proposé quand il y en a un ; la solution de chaque exercice est repliée : ne l’ouvrez qu’après avoir cherché.

Exercice 39.A Tester la durée de prêt maximaleEssentiel

Si vous avez fait l'exercice 40.A, écrivez un test : un média prêté 90 jours est accepté, un média prêté 91 jours est refusé.

Voir la solution
MediaServiceTest.java (extrait)·Java
    @Test
    void dureeDePretMaximale() {
        NewMedia limite = new NewMedia("Limite", "Auteur", 2000, MediaType.LIVRE, "Roman", null, 90);
        NewMedia trop = new NewMedia("Trop long", "Auteur", 2000, MediaType.LIVRE, "Roman", null, 91);

        // 90 : à la limite, accepté
        assertEquals(90, service.ajouter(limite).loanDays());
        // 91 : refusé
        assertThrows(IllegalArgumentException.class, () -> service.ajouter(trop));
    }
Ma confianceNon évalué

Exercice 39.B Tester les médias disponiblesEssentiel

Si vous avez fait l'exercice 40.B, écrivez un test : après avoir ajouté deux médias dont l'un est rendu indisponible, disponibles() ne renvoie qu'un élément, le média disponible.

Voir la solution
MediaServiceTest.java (extrait)·Java
    @Test
    void seulsLesMediasDisponiblesSontRenvoyes() {
        service.ajouter(dune());
        var blade = service.ajouter(new NewMedia("Blade Runner", "Ridley Scott", 1982, MediaType.FILM, "Science-fiction", 4.5, 7));
        service.changerDisponibilite(blade.id(), false);

        var disponibles = service.disponibles();

        assertEquals(1, disponibles.size());
        assertEquals("Dune", disponibles.get(0).title());
    }
Ma confianceNon évalué

Testez-vous : QCM

QCM · Chapitre 39

  1. Pourquoi un test du service peut-il s'exécuter sans base de données ?

  2. Que fait verify(dao, never()).ajouter(any()) ?

  3. Que signifie jdbc:h2:mem:test ?

  4. Quelle annotation marque une méthode comme test en JUnit 5 ?

  5. À quoi sert assertThrows ?

  6. Quelle assertion vérifie qu'une exception est bien levée ?

À faire : pour chaque question, expliquez la réponse avec vos propres mots, à voix haute ou par écrit, avant d’ouvrir l’explication. Si vous n’arrivez pas à la formuler, relisez le chapitre avant de continuer.