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.
- 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).
- 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 dossierformation-java. - 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
mainreste 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, puisgit branch: la branche active est précédée d'une étoile. - 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.
- Trouver un exercice
Dans le volet Project à gauche, ouvrez
srcpuis le chapitre, puis l'exercice : l'exercice 7.2.A se trouve danssrc > ch07 > ex7_2_a. OuvrezMain.java(ou le fichier cité dans l'énoncé) et complétez le code. - 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. - 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.
- 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.
- Un dossier
.ideadans le projet ?C'est la configuration IntelliJ (un petit fichier
.imlpar exercice, rangé dans.idea/modules). Ne le supprimez pas : IntelliJ en a besoin pour ouvrir le projet. - 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.
Archive ZIP · le projet complet avec son README · Node.js 22.22.3 ou plus récent
- Décompresser l'archive
Rangez le dossier
mediatechà côté de vos projets Java, pas dans « Téléchargements ». Le fichierREADME.mddu dossier reprend tout ce qui suit. - Ouvrir un terminal dans le dossier
mediatechDans IntelliJ : Open le dossier puis l'onglet Terminal. Ou dans VS Code, ou un terminal classique.
- Première fois seulement : installer les dépendances
npm install(quelques minutes). Vérifiez avant quenode -vaffiche 22.22.3 ou plus. - Terminal 1 : lancer le serveur de données
npm run api. Laissez ce terminal ouvert (port 3000). - Terminal 2 : lancer le site
Ouvrez un deuxième terminal, toujours dans
mediatech, puisnpm start. Attendez « Application bundle generation complete », puis ouvrez http://localhost:4200. - Essayer
Parcourez le catalogue, sélectionnez des médias, validez un emprunt. Comptes de test :
lecteur@example.cometbiblio@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
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 :
- la zone où l'on tape le code ;
- la console de sortie ;
- 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.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
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.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Testez-vous : QCM
QCM · Chapitre 1
Que signifie IDE ?
IDE signifie Integrated Development Environment : un éditeur de code qui regroupe tous les outils nécessaires.
À quoi sert le JDK ?
Le JDK contient le compilateur
javacet la JVM. Sans lui, un programme Java ne peut ni être compilé ni s'exécuter.Quel build system garde-t-on pour la formation ?
« IntelliJ » est le plus simple : l'IDE gère lui-même la compilation. Maven et Gradle s'apprennent plus tard.
Que se passe-t-il quand on clique sur le bouton vert ?
IntelliJ compile d'abord le code, puis l'exécute et affiche le résultat dans la console.
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 (
MainpourMain.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 ;
printlnpasse ensuite à la ligne,printnon. - 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 :
Bonjour !
Votre code à compléter
public class Main {
public static void main(String[] args) {
// Affichez le résultat ici
}
}
Voir la solution
public class Main {
public static void main(String[] args) {
System.out.println("Bonjour !");
}
}
Bonjour !
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
public class Main {
public static void main(String[] args) {
// Affichez le résultat ici
}
}
Voir la solution
public class Main {
public static void main(String[] args) {
System.out.println("Camille");
System.out.println("Paris");
}
}
Camille
Paris
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
public class Main {
public static void main(String[] args) {
// Affichez le résultat ici
}
}
Voir la solution
public class Main {
public static void main(String[] args) {
System.out.print("Java ");
System.out.print("est ");
System.out.print("super");
}
}
Java est super
Chaque mot garde un espace après lui, sinon ils seraient collés (Javaestsuper).
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 2.5.A Trouver l'erreur
Ce programme ne compile pas. Trouvez pourquoi et corrigez-le.
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.
public class Main {
public static void main(String[] args) {
System.out.println("Je suis un programme");
System.out.println("avec un petit oubli");
}
}
Je suis un programme
avec un petit oubli
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 2
Que fait
javac?javacest le compilateur. C'est ensuite la JVM qui exécute le bytecode produit.Quel est le rôle de la méthode
main?Sans point d'entrée, la JVM ne sait pas par où commencer.
Que se passe-t-il si on oublie un point-virgule à la fin d'une instruction ?
Une erreur de syntaxe empêche toute exécution : il faut la corriger d'abord.
Quelle différence entre
printetprintln?printlnajoute un retour à la ligne à la fin,printlaisse le curseur juste après le texte.
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
trueoufalse. 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
public class Main {
public static void main(String[] args) {
// Affichez le résultat ici
}
}
Voir la solution
public class Main {
public static void main(String[] args) {
System.out.println('J');
System.out.println("Java");
}
}
J
Java
La lettre seule utilise des guillemets simples, le mot utilise des guillemets doubles.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
public class Main {
public static void main(String[] args) {
// Affichez le résultat ici
}
}
Voir la solution
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);
}
}
Java
2026
3.14
false
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 3.3.B Quel type choisir ?
Pour chaque information, indiquez le type le plus adapté (int, double, char, boolean ou String) :
- le nombre de frères et sœurs ;
- le prix d'un café (1,80 €) ;
- l'initiale d'un prénom ;
- « l'utilisateur est connecté : oui ou non » ;
- le nom d'une ville.
Voir la solution
int: c'est un nombre entier.double: le prix a une virgule.char: un seul caractère.boolean: la réponse est vrai ou faux.String: c'est du texte.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Testez-vous : QCM
QCM · Chapitre 3
Quel type convient pour stocker le nombre
1.78?Un nombre à virgule se stocke dans un
double. Unintne garde que des nombres entiers.Que représente
'K'(avec des guillemets simples) ?Les guillemets simples désignent un seul caractère. Les guillemets doubles désignent du texte (
String).Quelles sont les deux seules valeurs possibles d'un
boolean?Un booléen ne vaut que
true(vrai) oufalse(faux), écrits en minuscules.Pourquoi
Strings'écrit-il avec une majuscule ?Les types primitifs s'écrivent en minuscules (
int,double…).Stringest une classe, d'où la 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 :
Je m'appelle Camille, j'ai 28 ans et je mesure 1.65 m.
Votre code à compléter
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
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.");
}
}
Je m'appelle Camille, j'ai 28 ans et je mesure 1.65 m.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
public class Main {
public static void main(String[] args) {
int score = 0;
System.out.println(score);
score = 50;
System.out.println(score);
}
}
0
50
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
public class Main {
public static void main(String[] args) {
int points = 10;
points = points + 5;
System.out.println("Points : " + points);
}
}
Points : 15
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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);
}
}
a = 8, b = 3
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 4
Que fait la ligne
int age = 30;?intest le type,agele nom, et= 30range la valeur dans la variable.Après
int age = 30;puisage = 20;, que vautage?On réaffecte la variable sans répéter le type : la nouvelle valeur remplace l'ancienne.
Quel nom de variable est valide en Java ?
Pas d'espace, pas de tiret, et on ne commence pas par un chiffre.
prixTotalsuit la convention camelCase.Quel caractère passe à la ligne dans un texte ?
C'est l'antislash suivi de la lettre n :
\n.
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 % 3vaut1. - Division entière
- Division de deux
int: la partie décimale est supprimée (9 / 5vaut1). - 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.99donne9. - 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
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
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);
}
}
Périmètre : 26
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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);
}
}
17 % 5 = 2
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
public class Main {
public static void main(String[] args) {
int compteur = 10;
compteur++;
System.out.println(compteur);
compteur--;
System.out.println(compteur);
}
}
11
10
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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);
}
}
Moyenne : 13.5
En écrivant 2.0 au lieu de 2, la division se fait en décimal. Avec 2, on obtiendrait 13.0.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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);
}
}
Le code ASCII de A est 65
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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 :
Stylo 1,50 €
Cahier 3,20 €
Classeur 12,00 €
Votre code à compléter
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.
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);
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 5
Que renvoie
9 / 5si les deux nombres sont desint?Division entière : la partie décimale est tronquée, sans arrondi.
Que fait l'opérateur
%?10 % 3vaut1: c'est le reste quand on divise 10 par 3.Que fait
(int) lettresilettreest unchar?C'est un cast :
(int) 'A'donne 65.Que fait l'instruction
compteur++;?compteur++est le raccourci decompteur = compteur + 1.
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 unnextLine(). - 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
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
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
7
Vous avez saisi : 7
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
3
4
La somme est : 7
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
3
5
L'aire est : 15.0
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
Camille
Bonjour, Camille !
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 6.6.A Synthèse : nom, âge, ville
Lisez trois informations :
- le nom complet, avec
nextLine(); - l'âge, avec
nextInt(); - la ville, avec
nextLine().
Pensez à vider le buffer entre l'âge et la ville. Affichez chaque information sur sa ligne :
Nom : Camille
Âge : 28
Ville : Paris
Votre code à compléter
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
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
Camille
28
Paris
Nom : Camille
Âge : 28
Ville : Paris
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 6
Que faut-il écrire en haut du fichier pour utiliser
Scanner?Scannern'est pas dansjava.lang: il faut l'importer.Quelle différence entre
next()etnextLine()?Pour une phrase avec des espaces, il faut
nextLine().Que se passe-t-il si on appelle
scanner.nextLine()juste aprèsscanner.nextInt(), sans précaution ?nextInt()laisse le retour à la ligne dans le buffer : il faut le consommer avec unnextLine()à vide.Que représente
System.in?System.outdésigne la console de sortie,System.inl'entrée clavier.
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é
- Demander le prénom du voyageur, puis afficher un message de bienvenue.
- Demander la distance jusqu'à l'aéroport en km, la convertir en miles, afficher.
- Demander la température prévue à New York en °C, la convertir en °F, afficher.
- 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
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
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
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Partie 2 · 2 / 14
Décider et répéter
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
Stringne 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
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
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
7
Impair
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
20
Majeur
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 7.4.A Positif, négatif ou nul
Lisez un entier. Affichez Positif, Négatif ou Nul.
Votre code à compléter
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
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
-4
Négatif
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
13
Bien
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
secret
Accès autorisé
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 7.6.A Initiale et longueur
Lisez un prénom avec nextLine(). Affichez son initiale et sa longueur (si l'utilisateur tape Camille) :
Initiale : C
Longueur : 7
Votre code à compléter
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
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
Camille
Initiale : C
Longueur : 7
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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…), affichezC'est parti !; - sinon, affichez
Tant pis.
Indice : strip() et equalsIgnoreCase.
Votre code à compléter
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
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
Oui
C'est parti !
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 7.6.C Le domaine d'une adresse e-mail
Lisez une adresse e-mail :
- affichez ce qui suit le
@:Domaine : exemple.frsi l'utilisateur tapecamille@exemple.fr; - s'il n'y a pas de
@, affichezAdresse invalide.
Indice : indexOf renvoie -1 quand il ne trouve rien, et substring(debut) prend la fin du texte.
Votre code à compléter
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
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
camille@exemple.fr
Domaine : exemple.fr
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 7
Quelle écriture compare deux valeurs ?
=range une valeur,==compare.Comment comparer correctement deux
String?equalscompare le contenu du texte.Avec
if (n >= 10) {…} else if (n >= 16) {…}, que se passe-t-il pour n = 18 ?La première condition vraie gagne : le
else ifn'est jamais testé.Que vaut
7 != 7?!=signifie « différent de » : 7 n'est pas différent de 7.Que vaut
"Bonjour".substring(0, 3)?De l'indice 0 jusqu'à l'indice 3 exclu : les indices 0, 1 et 2, donc
"Bon".Après
String s = "java"; s.toUpperCase();, que vauts?Un
Stringne change jamais :toUpperCase()renvoie un nouveau texte, perdu ici. Il faudrait écrires = s.toUpperCase();.
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: unif/elseécrit en une ligne.switch- Compare une variable à plusieurs valeurs précises (
case). break- Sort du
switch. Sans lui, Java continue dans lecasesuivant. 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
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
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
15
Adolescent
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
- Avec un
Scanner, lisez trois entiers avecnextInt()et rangez-les dans trois variables (a,betc). - Comparez-les avec des
if/else if/else, en combinant deux comparaisons avec&&. Par exemple,aest le plus grand siaest supérieur ou égal àbet àc. - 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
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
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
8
3
5
Le plus grand : 8
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
9
9 est impair
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
3
Mercredi
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 8
Que vaut
true && false?&&n'est vrai que si les deux côtés sont vrais.Quand
a || best-il vrai ?||est le OU logique.Que se passe-t-il si on oublie
breakdans uncase?C'est le comportement « fall-through » du
switchclassique.Que vaut
(5 > 3) ? "oui" : "non"?La condition est vraie, donc le ternaire renvoie la première valeur.
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.
- Demander le prénom, puis la durée de sa course en minutes (nombre entier) pour 10 km.
- Calculer l'allure : minutes par kilomètre (
duree / 10.0). - Afficher un niveau : moins de 40 min →
Expert, moins de 55 min →Confirmé, moins de 70 min →Régulier, sinonDébutant. - 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
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
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
Prénom : Camille
Durée du 10 km (en minutes) : 52
Camille, votre allure est de 5.2 min/km
Niveau : Confirmé
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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/continuebreakquitte la boucle ;continuepasse 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
public class Main {
public static void main(String[] args) {
// Écrivez votre boucle ici (for)
// Affichez le résultat ici
}
}
Voir la solution
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 !");
}
}
5
4
3
2
1
Décollage !
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 9.3.A Compter jusqu'à 10
Affichez les nombres de 1 à 10, un par ligne, avec une boucle for.
Votre code à compléter
public class Main {
public static void main(String[] args) {
// Écrivez votre boucle ici (for)
}
}
Voir la solution
public class Main {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
System.out.println(i);
}
}
}
1
2
3
4
5
6
7
8
9
10
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
public class Main {
public static void main(String[] args) {
// Écrivez votre boucle ici (for)
}
}
Voir la solution
public class Main {
public static void main(String[] args) {
for (int i = 2; i <= 20; i += 2) {
System.out.println(i);
}
}
}
2
4
6
8
10
12
14
16
18
20
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
Un nombre entre 1 et 10 : 15
Un nombre entre 1 et 10 : 7
Merci !
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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);
}
}
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).
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 9
Quelle boucle s'exécute au moins une fois, même si la condition est fausse dès le départ ?
do…whileteste sa condition après le premier tour.Que fait
breakdans une boucle ?continuepasse au tour suivant,breakquitte la boucle.Que provoque l'oubli de
compteur++dans unwhile (compteur <= 5)?La condition ne change jamais : elle reste vraie.
Combien de fois s'exécute
for (int i = 0; i < 4; i++)?i vaut 0, 1, 2 et 3 : quatre tours.
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.
- Le code secret est
4821(variableint). - Tant qu'il reste des essais, demander le code.
- Si le code est bon : afficher
Coffre ouvert !et sortir de la boucle. - Sinon, afficher le nombre d'essais restants.
- Après 3 échecs, afficher
Coffre verrouillé.
Réalisez le coffre-fort.
Votre code à compléter
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
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
Code (essai 1/3) : 1111
Mauvais code. Essais restants : 2
Code (essai 2/3) : 4821
Coffre ouvert !
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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 :
===== 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 unswitchsur 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: afficherChoix 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
===== 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
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.
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();
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Partie 3 · 3 / 14
Stocker plusieurs données
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,falseounull.
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
public class Main {
public static void main(String[] args) {
// Créez votre tableau ici : prenoms
// Affichez le résultat ici
}
}
Voir la solution
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]);
}
}
Premier : Léa
Dernier : Noé
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
public class Main {
public static void main(String[] args) {
int[] valeurs = {5, 10, 15, 20};
for (int valeur : valeurs) {
System.out.println(valeur);
}
}
}
5
10
15
20
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
Note 1 : 12
Note 2 : 15
Note 3 : 9
Note 4 : 18
Moyenne : 13.5
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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);
}
}
108
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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);
}
}
Minimum : 3
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 10
Quel est l'indice du premier élément d'un tableau ?
En Java, les indices commencent toujours à 0.
Que renvoie
tableau.lengthpourint[] tableau = {3, 6, 9};?lengthest le nombre de cases du tableau.Que se passe-t-il avec
notes[5]dans un tableau de 5 cases ?Les indices valides vont de 0 à 4.
Peut-on ajouter une 6ᵉ case à un tableau de 5 cases ?
C'est la limite des tableaux : les collections (chapitre suivant) la résolvent.
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.
- Créer le tableau
double[] temperatures = {14.5, 16.0, 12.5, 18.0, 20.5, 19.0, 15.5};. - Afficher chaque relevé avec le numéro du jour.
- 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
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
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);
}
}
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
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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 (
Integerpourint), 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
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
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);
}
}
}
- Pain
- Lait
- Pommes
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
}
}
3
2
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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);
}
}
Moyenne : 13.5
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
}
}
3
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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"));
}
}
Numéro de Hugo : 06 98 76 54 32
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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));
}
}
}
pommes : 3
poires : 5
L'ordre d'affichage peut varier : c'est normal avec un HashMap.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 11
Quelle collection choisir pour une liste sans doublons ?
Un
HashSetignore les éléments déjà présents.Quelle collection associe un nom à un numéro de téléphone ?
HashMapassocie une clé (nom) à une valeur (numéro).Pourquoi écrit-on
ArrayList<Integer>et nonArrayList<int>?Integerest la classe enveloppe deint; la conversion est automatique (autoboxing).Que renvoie
liste.get(0)?Les indices commencent à 0, comme pour les tableaux.
Dans quel ordre un
HashMapdonne-t-il ses éléments ?Pour garder l'ordre d'ajout :
LinkedHashMap. Pour un tri :TreeMap.
Partie 4 · 4 / 14
Organiser son code et son texte
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
public class Main {
// Méthode à écrire : static void afficherBienvenue()
public static void main(String[] args) {
// Complétez ici
}
}
Voir la solution
public class Main {
static void afficherBienvenue() {
System.out.println("Bienvenue à la formation Java !");
}
public static void main(String[] args) {
afficherBienvenue();
afficherBienvenue();
}
}
Bienvenue à la formation Java !
Bienvenue à la formation Java !
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
}
}
12
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
public class Main {
static int doubler(int n) {
return n * 2;
}
public static void main(String[] args) {
System.out.println(doubler(21));
}
}
42
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
}
}
13.0
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 12.5.A Majeur ?
Écrivez static boolean estMajeur(int age). Testez avec 17 et 18.
Votre code à compléter
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
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
}
}
false
true
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 12
Que signifie
voiddevant une méthode ?void= « vide » : pas de résultat renvoyé.Que fait
return?Le code placé après
returnn'est jamais exécuté.Où écrit-on une méthode ?
Les méthodes sont des membres de la classe.
Une variable déclarée dans la méthode A est-elle visible dans la méthode B ?
On transmet les valeurs via les paramètres et
return.
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.
static double avecTva(double prixHt): renvoie le prix TTC (prixHt * 1.055), arrondi au centime.- Dans
main: lire le prix HT, appeler la méthode et afficher le résultat.
Réalisez la caisse.
Votre code à compléter
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
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
Prix HT (€) : 2
À payer : 2.11 €
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
Stringne 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 (%dentier,%.2fdécimal à 2 chiffres,%stexte). - 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
matchesoureplaceAll.
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
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
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);
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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));
}
}
1-2-3-4-5
1-2-3-4-5-6-7-8-9-10
1
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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);
}
}
==================================
Bonjour Hugo, vous avez 120 points
==================================
1. Commander
2. Voir mes points
3. Quitter
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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"));
}
}
true
true
false
reverse() appartient à StringBuilder, pas à String : on passe par lui, puis on revient à une String avec toString().
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 13
Que vaut
nomaprèsString nom = "léa"; nom.toUpperCase();?Une
Stringne change jamais : le résultat detoUpperCase()est perdu s'il n'est pas rangé.Pourquoi utiliser un
StringBuilderdans une boucle ?Le
StringBuilderse modifie sur place ; on appelletoString()à la fin.Que donne
String.format("[%5d]", 42)?%5d: 5 caractères, alignés à droite.%-5dalignerait à gauche.Sur une machine en français,
"%.2f".formatted(2.5)donne…Le format suit la langue de l'ordinateur ;
Locale.USforce le point.Que renvoie
" ".isBlank()?isBlank()accepte les espaces ;isEmpty(), lui, renverraitfalse.
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[]) :
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 :
===== 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
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.
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);
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Partie 5 · 5 / 14
Les classes et les objets
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.pseudoest 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
public class Chat {
// Constructeur : Chat(String nom, int age)
}
public class Main {
public static void main(String[] args) {
// Créez votre objet ici : felix
// Affichez le résultat ici
}
}
Voir la solution
public class Chat {
String nom;
int age;
Chat(String nom, int age) {
this.nom = nom;
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Chat felix = new Chat("Félix", 3);
System.out.println(felix.nom);
}
}
Félix
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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.");
}
}
}
Félix dit : Miaou !
Luna dit : Miaou !
Luna est le plus âgé.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Exercice 14.3.B Un compteur
Créez une classe Compteur :
- un attribut
valeur(unint, qui vaut 0 au départ) ; - trois méthodes :
incrementer()ajoute 1 ;reinitialiser()remet à 0 ;afficher()écritCompteur : <valeur>.
Dans main, créez deux compteurs, incrémentez le premier trois fois et le second une fois, puis affichez-les :
Compteur : 3
Compteur : 1
Votre code à compléter
public class Compteur {
// Méthode à écrire : void incrementer()
// Méthode à écrire : void reinitialiser()
// Méthode à écrire : void afficher()
}
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
public class Compteur {
int valeur = 0;
void incrementer() {
valeur++;
}
void reinitialiser() {
valeur = 0;
}
void afficher() {
System.out.println("Compteur : " + valeur);
}
}
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();
}
}
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;).
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
public class Chaton {
String nom;
int age;
Chaton(String nom, int age) {
this.nom = nom;
this.age = age;
}
}
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
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.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
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
public class Chat {
// Constructeur : Chat(String nom, int age)
// Méthode à écrire : void feterAnniversaire()
}
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
public class Chat {
String nom;
int age;
Chat(String nom, int age) {
this.nom = nom;
this.age = age;
}
void feterAnniversaire() {
age++;
}
}
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
}
}
5
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
public class Chat {
// Constructeur : Chat(String nom, int age)
// Méthode à écrire : void miauler()
}
public class Main {
public static void main(String[] args) {
// Créez votre objet ici : felix
// Appelez la méthode miauler() ici
}
}
Voir la solution
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 !");
}
}
public class Main {
public static void main(String[] args) {
Chat felix = new Chat("Félix", 3);
felix.miauler();
}
}
Félix dit : Miaou !
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 14.5.B Un rectangle
Créez une classe Rectangle :
- deux attributs
largeuretlongueur(desdouble) ; - un constructeur qui les initialise ;
- deux méthodes
aire()etperimetre(), qui renvoient undouble.
Dans main, créez un rectangle de 4 sur 5 et affichez son aire et son périmètre :
Aire : 20.0
Périmètre : 18.0
Votre code à compléter
public class Rectangle {
// Constructeur : Rectangle(double largeur, double longueur)
// Méthode à écrire : double aire()
// Méthode à écrire : double perimetre()
}
public class Main {
public static void main(String[] args) {
// Créez votre objet ici : r
// Affichez le résultat ici
}
}
Voir la solution
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);
}
}
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());
}
}
Aire : 20.0
Périmètre : 18.0
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 14.6.A Un élève avec deux constructeurs
Créez une classe Eleve :
- deux attributs :
prenom(String) etage(int) ; - deux constructeurs :
- un complet,
Eleve(String prenom, int age); - un pratique,
Eleve(String prenom), qui donne 18 ans par défaut ;
- un complet,
- une méthode
sePresenter()qui afficheJe suis <prenom>, <age> ans.
Créez Camille (18 ans par défaut) et Hugo (22 ans) :
Je suis Camille, 18 ans.
Je suis Hugo, 22 ans.
Votre code à compléter
public class Eleve {
// Constructeur : Eleve(String prenom, int age)
// Constructeur : Eleve(String prenom)
// Méthode à écrire : void sePresenter()
}
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
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.");
}
}
public class Main {
public static void main(String[] args) {
Eleve camille = new Eleve("Camille");
Eleve hugo = new Eleve("Hugo", 22);
camille.sePresenter();
hugo.sePresenter();
}
}
Je suis Camille, 18 ans.
Je suis Hugo, 22 ans.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 14
Quelle est la différence entre une classe et un objet ?
Une classe
Joueurpermet de créer autant d'objets joueurs que l'on veut.Quel mot-clé crée un objet ?
new Joueur(...)appelle le constructeur et construit l'objet.Que désigne
thisdans un constructeur ?this.pseudoest l'attribut,pseudoseul est le paramètre.Un constructeur a…
C'est ce qui permet à Java de le reconnaître.
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
public class CarteCadeau {
// Attributs : solde
// Constructeur : CarteCadeau(double solde)
// Méthode à écrire : public double getSolde()
// Méthode à écrire : public void depenser(double montant)
}
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
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.");
}
}
}
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
}
}
Solde insuffisant.
30.0
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
public void setTitre(String titre) {
if (!titre.isEmpty()) {
this.titre = titre;
} else {
System.out.println("Titre invalide.");
}
}
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Exercice 15.4.A Le thermostat
Créez une classe Thermostat :
- un attribut privé
temperature(unint, 19 au départ) ; - un getter ;
- un setter
setTemperature(int temperature)qui n'accepte que les valeurs entre 5 et 30 ; sinon, il afficheTempérature refusée : <valeur>et ne change rien.
Testez avec 22, puis 50 :
Température : 22
Température refusée : 50
Température : 22
Votre code à compléter
public class Thermostat {
// Attributs : temperature
// Méthode à écrire : public int getTemperature()
// Méthode à écrire : public void setTemperature(int temperature)
}
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
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);
}
}
}
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());
}
}
Température : 22
Température refusée : 50
Température : 22
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
}
}
Retrait de 500 accepté ? false
Solde : 120.0
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Exercice 15.5.B Un livret d'épargne
Créez une classe Livret :
- un attribut privé
solde(undouble), reçu par le constructeur ; - un getter ;
- deux méthodes
deposer(double montant)etretirer(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.
Solde insuffisant.
Solde : 120.0 €
Votre code à compléter
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)
}
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
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;
}
}
}
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() + " €");
}
}
Solde insuffisant.
Solde : 120.0 €
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 15.5.C Un mot de passe bien gardé
Créez une classe Compte :
- un attribut privé
motDePasse(unString), sans getter ; - un constructeur qui reçoit le mot de passe ;
- une méthode
boolean verifier(String essai)qui renvoietruesi 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 ?
true
false
Votre code à compléter
public class Compte {
// Attributs : motDePasse
// Constructeur : Compte(String motDePasse)
// Méthode à écrire : public boolean verifier(String essai)
}
public class Main {
public static void main(String[] args) {
// Créez votre objet ici : compte
// Affichez le résultat ici
}
}
Voir la solution
public class Compte {
private String motDePasse;
public Compte(String motDePasse) {
this.motDePasse = motDePasse;
}
public boolean verifier(String essai) {
return motDePasse.equals(essai);
}
}
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"));
}
}
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).
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 15
Pourquoi déclarer un attribut
private?L'accès se fait alors via des méthodes qui contrôlent les valeurs.
À quoi sert un getter ?
Un getter renvoie la valeur sans permettre de la modifier.
Quel est l'intérêt principal d'un setter ?
Par exemple, refuser un solde négatif.
Que signifie
private?privateest le niveau d'accès le plus restrictif.
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
Stringqui 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
public class Visiteur {
// Attributs : total, nom
// Constructeur : Visiteur(String nom)
// Méthode à écrire : public static int getTotal()
}
public class Main {
public static void main(String[] args) {
// Créez vos objets ici
// Affichez le résultat ici
}
}
Voir la solution
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;
}
}
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
}
}
4
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
public class Outils {
// Méthode à écrire : public static int carre(int n)
}
public class Main {
public static void main(String[] args) {
// Affichez le résultat ici
}
}
Voir la solution
public class Outils {
public static int carre(int n) {
return n * n;
}
}
public class Main {
public static void main(String[] args) {
System.out.println(Outils.carre(7)); // 49
}
}
49
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
public class Main {
public static void main(String[] args) {
double rayon = 3;
double aire = Math.PI * rayon * rayon;
System.out.println("Aire : " + aire);
}
}
Aire : 28.274333882308138
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
public class Jeu {
// Attributs : VIES_MAX, POINTS_PAR_PIECE
}
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
public class Jeu {
public static final int VIES_MAX = 3;
public static final int POINTS_PAR_PIECE = 10;
}
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);
}
}
Score : 70
Vies : 3
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 16
À qui appartient un attribut
static?Il n'existe qu'en un seul exemplaire.
Dans la forme classique, pourquoi
mainest-ilstatic?La JVM doit l'appeler sans créer d'objet. Depuis Java 25, un
mainsansstaticest aussi accepté : la JVM crée alors l'objet elle-même.Comment appelle-t-on une méthode statique
sqrtde la classeMath?On utilise le nom de la classe.
Que signifie
static final?Par convention, le nom est en MAJUSCULES.
Que contient le tableau
argsquand on lance le programme sans rien ajouter ?argsn'est jamaisnull: c'est simplement un tableau sans case.
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
staticpour 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 :
Livre:- un titre ;
- son getter.
Adherent:- un nom ;
- une méthode
emprunter(Livre livre)qui afficheLéa emprunte « Le Petit Prince ».
Mettez le Main dans le package fr.bibliotheque.
Votre code à compléter
public class Livre {
// Attributs : titre
// Constructeur : Livre(String titre)
// Méthode à écrire : public String getTitre()
}
public class Adherent {
// Attributs : nom
// Constructeur : Adherent(String nom)
// Méthode à écrire : public void emprunter(Livre livre)
}
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 :
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) :
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 :
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"));
}
}
Léa emprunte « Le Petit Prince »
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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 *.
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 :
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 * :
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);
}
}
true
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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);
}
}
Hypoténuse : 5.0
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 17
Où doit se trouver la classe déclarée
package fr.boulangerie.caisse;?Le dossier doit correspondre au package, point par point.
Quelle classe s'utilise sans import ?
Mathest dansjava.lang, importé d'office.Avec
import java.awt.*;etimport java.util.*;, que donneList<String> noms;?Il faut un import précis (
import java.util.List;) ou le nom complet.À quoi sert
import static java.lang.Math.max;?import staticimporte un membrestaticd'une classe.Une classe déclarée
class Recette, sanspublic, est visible…C'est l'accès package : sans mot-clé.
En Java, un membre
protectedest visible…C'est une différence avec PHP, où
protectedne concerne que l'héritage.
Partie 6 · 6 / 14
Les dates
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 avecLocalDate.of(année, mois, jour)ouLocalDate.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.MMest le mois,mmles 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
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
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);
}
}
À rendre le : 2026-11-09
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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");
}
}
}
En retard
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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));
}
}
Rentrée : 01/09/2026
Vacances : 21/10/2026
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 18
Que contient
empruntaprèsemprunt.plusDays(21);?Une
LocalDateest immuable :plusDaysrenvoie une nouvelle date qu'il faut garder.Quel mois désigne
LocalDate.of(2026, 9, 1)?Les mois de
java.timevont de 1 (janvier) à 12 (décembre).Comment savoir si la date
aest avant la dateb?Les opérateurs
<et>ne servent que pour les nombres ; pour les dates,isBeforeetisAfter.Quel motif affiche la date
01/09/2026?MMest le mois ;mmdésigne les minutes.Quel type choisir pour une date de naissance ?
LocalDatereprésente une date seule ; il sait la comparer, la calculer et l'afficher.
Partie 7 · 7 / 14
Héritage, classes abstraites et interfaces
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
public class Oiseau extends Animal {
// Constructeur : Oiseau(String nom, int age)
// Méthode à écrire : public void crier()
}
public class Main {
public static void main(String[] args) {
// Créez votre objet ici : piou
// Appelez la méthode crier() ici
}
}
Voir la solution
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 !");
}
}
public class Main {
public static void main(String[] args) {
Oiseau piou = new Oiseau("Piou", 1);
piou.crier();
}
}
Piou chante : Cui-cui !
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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().
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Exercice 19.3.B Compléter la méthode de la mère avec super
Créez deux classes :
Personne:- un attribut
prenom; - une méthode
sePresenter()qui afficheJe m'appelle <prenom>.
- un attribut
Etudiant, qui hérite dePersonne:- un attribut
filiere; sePresenter()redéfinie pour d'abord appeler la version de la mère (super.sePresenter()), puis afficherJ'étudie : <filiere>.
- un attribut
Testez avec Inès, filière Informatique :
Je m'appelle Inès.
J'étudie : Informatique.
Votre code à compléter
public class Personne {
// Attributs : prenom
// Constructeur : Personne(String prenom)
// Méthode à écrire : public void sePresenter()
}
public class Etudiant extends Personne {
// Attributs : filiere
// Constructeur : Etudiant(String prenom, String filiere)
// Méthode à écrire : public void sePresenter()
}
public class Main {
public static void main(String[] args) {
// Créez votre objet ici : ines
// Appelez la méthode sePresenter() ici
}
}
Voir la solution
public class Personne {
protected String prenom;
public Personne(String prenom) {
this.prenom = prenom;
}
public void sePresenter() {
System.out.println("Je m'appelle " + prenom + ".");
}
}
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
}
}
public class Main {
public static void main(String[] args) {
Etudiant ines = new Etudiant("Inès", "Informatique");
ines.sePresenter();
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 19.4.A Trouver la faute
Cette classe ne compile pas. Pourquoi ? Corrigez-la.
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().
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Exercice 19.5.A Des formes géométriques
Créez trois classes :
Forme: une méthodeaire()qui renvoie0;Rectangle(largeur, longueur) : redéfinitaire();Cercle(rayon) : redéfinitaire().
Affichez l'aire d'un rectangle 4×5 (20.0) et d'un cercle de rayon 2.
Votre code à compléter
public class Forme {
// Méthode à écrire : public double aire()
}
public class Rectangle extends Forme {
// Attributs : largeur, longueur
// Constructeur : Rectangle(double largeur, double longueur)
// Méthode à écrire : public double aire()
}
public class Cercle extends Forme {
// Attributs : rayon
// Constructeur : Cercle(double rayon)
// Méthode à écrire : public double aire()
}
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
public class Forme {
public double aire() {
return 0;
}
}
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;
}
}
public class Cercle extends Forme {
private double rayon;
public Cercle(double rayon) {
this.rayon = rayon;
}
@Override
public double aire() {
return Math.PI * rayon * rayon;
}
}
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());
}
}
}
Aire : 20.0
Aire : 12.566370614359172
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 19.5.B Des instruments de musique
Créez trois classes :
Instrument:- un attribut
nom; - une méthode
jouer()qui affiche<nom> fait un son.
- un attribut
Piano, fille d'Instrument, qui redéfinitjouer()pour afficher<nom> : do ré mi !;Guitare, fille d'Instrument, qui redéfinitjouer()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 :
Piano à queue : do ré mi !
Folk : ♪ strum strum ♪
Triangle fait un son.
Votre code à compléter
public class Instrument {
// Attributs : nom
// Constructeur : Instrument(String nom)
// Méthode à écrire : public void jouer()
}
public class Piano extends Instrument {
// Constructeur : Piano(String nom)
// Méthode à écrire : public void jouer()
}
public class Guitare extends Instrument {
// Constructeur : Guitare(String nom)
// Méthode à écrire : public void jouer()
}
public class Main {
public static void main(String[] args) {
// Complétez ici
// Parcourez avec une boucle for-each ici
}
}
Voir la solution
public class Instrument {
protected String nom;
public Instrument(String nom) {
this.nom = nom;
}
public void jouer() {
System.out.println(nom + " fait un son.");
}
}
public class Piano extends Instrument {
public Piano(String nom) {
super(nom);
}
@Override
public void jouer() {
System.out.println(nom + " : do ré mi !");
}
}
public class Guitare extends Instrument {
public Guitare(String nom) {
super(nom);
}
@Override
public void jouer() {
System.out.println(nom + " : ♪ strum strum ♪");
}
}
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
}
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 19.5.C La facture polymorphe
Un magasin vend des Article (TVA à 20 %) et des Livre (TVA à 5,5 %).
Article:- les attributs
nometprixHt; - une méthode
prixTtc()qui renvoieprixHt + prixHt * 0.2.
- les attributs
Livre, qui hérite d'Articleet redéfinitprixTtc()avecprixHt + 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 :
Stylo : 12.0 €
Roman : 21.1 €
Sac : 60.0 €
Total : 93.1 €
Votre code à compléter
public class Article {
// Attributs : nom, prixHt
// Constructeur : Article(String nom, double prixHt)
// Méthode à écrire : public String getNom()
// Méthode à écrire : public double prixTtc()
}
public class Livre extends Article {
// Constructeur : Livre(String nom, double prixHt)
// Méthode à écrire : public double prixTtc()
}
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
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;
}
}
public class Livre extends Article {
public Livre(String nom, double prixHt) {
super(nom, prixHt);
}
@Override
public double prixTtc() {
return prixHt + prixHt * 0.055;
}
}
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 + " €");
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
public class Animal {
public void parler() {
System.out.println("...");
}
}
public class Chien extends Animal {
@Override
public void parler() {
System.out.println("Wouf");
}
public void garder() {
System.out.println("Je garde la maison.");
}
}
public class Chiot extends Chien {
@Override
public void parler() {
System.out.println("Yip !");
}
}
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
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();.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Testez-vous : QCM
QCM · Chapitre 19
Que se passe-t-il si on écrit
@Overridesur une méthode qui n'existe pas dans la classe mère ?C'est une erreur de compilation, pas une exception d'exécution. C'est précisément le rôle de
@Override.Quel mot-clé exprime l'héritage ?
class Chien extends Animal.À quoi sert
super(nom, age)dans un constructeur de classe fille ?Il doit être la première instruction du constructeur.
Que fait
@Override?Le compilateur vérifie que la méthode existe bien dans la mère.
Soit
Animal a = new Chien("Rex", 4); a.crier();— quelle version decrier()est appelée ?C'est le polymorphisme : l'objet réel détermine la version exécuté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 abstraitepayer(double montant); - deux classes filles :
CarteBancaire: affichePaiement de X € par carte;Especes: affichePaiement de X € en espèces.
Testez avec une boucle sur un tableau de Paiement.
Votre code à compléter
public abstract class Paiement {
// Méthode abstraite à déclarer : public abstract void payer(double montant)
}
public class CarteBancaire extends Paiement {
// Méthode à écrire : public void payer(double montant)
}
public class Especes extends Paiement {
// Méthode à écrire : public void payer(double montant)
}
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
public abstract class Paiement {
public abstract void payer(double montant);
}
public class CarteBancaire extends Paiement {
@Override
public void payer(double montant) {
System.out.println("Paiement de " + montant + " € par carte");
}
}
public class Especes extends Paiement {
@Override
public void payer(double montant) {
System.out.println("Paiement de " + montant + " € en espèces");
}
}
public class Main {
public static void main(String[] args) {
Paiement[] moyens = { new CarteBancaire(), new Especes() };
for (Paiement moyen : moyens) {
moyen.payer(12.5);
}
}
}
Paiement de 12.5 € par carte
Paiement de 12.5 € en espèces
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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:envoyerafficheEmail : <message>;Sms:envoyerafficheSMS : <message>.
Dans main, envoyez Votre colis arrive. à Léa par email et à Hugo par SMS, en appelant afficherEntete() avant chaque envoi :
--- Pour Léa ---
Email : Votre colis arrive.
--- Pour Hugo ---
SMS : Votre colis arrive.
Votre code à compléter
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()
}
public class Email extends Notification {
// Constructeur : Email(String destinataire)
// Méthode à écrire : public void envoyer(String message)
}
public class Sms extends Notification {
// Constructeur : Sms(String destinataire)
// Méthode à écrire : public void envoyer(String message)
}
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
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 + " ---");
}
}
public class Email extends Notification {
public Email(String destinataire) {
super(destinataire);
}
@Override
public void envoyer(String message) {
System.out.println("Email : " + message);
}
}
public class Sms extends Notification {
public Sms(String destinataire) {
super(destinataire);
}
@Override
public void envoyer(String message) {
System.out.println("SMS : " + message);
}
}
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
}
}
}
--- Pour Léa ---
Email : Votre colis arrive.
--- Pour Hugo ---
SMS : Votre colis arrive.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
public abstract class Boisson {
protected String nom;
public Boisson(String nom) {
this.nom = nom;
}
public abstract double prix();
}
// ne compile pas
public class Cafe extends Boisson {
public Cafe() {
super("Café");
}
}
Cafe.java:1: error: Cafe is not abstract and does not override abstract method prix() in Boisson
public class Cafe extends Boisson {
^
// ne compile pas
public class Main {
public static void main(String[] args) {
Boisson b = new Boisson("Eau");
}
}
Main.java:3: error: Boisson is abstract; cannot be instantiated
Boisson b = new Boisson("Eau");
^
Voir la solution
- Première erreur :
Cafehérite deBoissonmais n'écrit pas la méthode abstraiteprix(). Une classe concrète doit écrire toutes les méthodes abstraites de sa mère. Correction : ajouterprix()dansCafe(ou rendreCafeabstraite, ce qui n'aurait pas de sens ici). - Seconde erreur : on ne peut pas faire
newsur une classe abstraite. Correction : créer un objet d'une fille (new Cafe()), qu'on peut ranger dans une variable de typeBoisson.
public class Cafe extends Boisson {
public Cafe() {
super("Café");
}
@Override
public double prix() {
return 1.8;
}
}
public class Main {
public static void main(String[] args) {
Boisson b = new Cafe();
System.out.println(b.nom + " : " + b.prix() + " €");
}
}
Café : 1.8 €
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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()renvoiejours * tarif.
Affichez un salarié à 2800 € et un freelance qui a travaillé 12 jours à 250 €/jour :
Nora : 2800.0 €
Ilyès : 3000.0 €
Votre code à compléter
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()
}
public class Salarie extends Employe {
// Attributs : salaireFixe
// Constructeur : Salarie(String nom, double salaireFixe)
// Méthode à écrire : public double salaire()
}
public class Freelance extends Employe {
// Attributs : jours, tarifParJour
// Constructeur : Freelance(String nom, int jours, double tarifParJour)
// Méthode à écrire : public double salaire()
}
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
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() + " €");
}
}
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;
}
}
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;
}
}
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();
}
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 20
Peut-on écrire
new Vehicule("x")siVehiculeest abstraite ?On instancie ses classes filles.
Une méthode abstraite…
Les classes filles doivent l'implémenter.
Que doit faire une classe fille concrète d'une classe abstraite ?
Sinon la compilation échoue.
Une classe abstraite peut-elle contenir des méthodes classiques ?
Elle mélange librement code partagé et méthodes à définir.
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
public class Humain implements Nageur {
// Méthode à écrire : public void nager()
}
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
public class Humain implements Nageur {
@Override
public void nager() {
System.out.println("Un humain nage la brasse.");
}
}
public class Main {
public static void main(String[] args) {
Nageur[] bassin = { new Canard("Donald"), new Poisson(), new Humain() };
for (Nageur n : bassin) {
n.nager();
}
}
}
Donald nage sur l'étang.
Le poisson nage sous l'eau.
Un humain nage la brasse.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Exercice 21.2.B Deux contrats pour un seul objet
Créez :
- deux interfaces :
Volant(méthodevoler()) etPlongeur(méthodeplonger()) ; Canard, qui implémente les deux ;Avion, qui n'implémente queVolant.
Dans main :
- rangez un
Canardet unAviondans uneArrayList<Volant>et faites-les voler ; - faites plonger un
Canardrangé dans une variable de typePlongeur.
Le canard bat des ailes.
L'avion décolle.
Le canard plonge dans l'étang.
Votre code à compléter
public interface Volant {
// Méthode abstraite à déclarer : void voler()
}
public interface Plongeur {
// Méthode abstraite à déclarer : void plonger()
}
public class Canard implements Volant, Plongeur {
// Méthode à écrire : public void voler()
// Méthode à écrire : public void plonger()
}
public class Avion implements Volant {
// Méthode à écrire : public void voler()
}
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
public interface Volant {
void voler();
}
public interface Plongeur {
void plonger();
}
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.");
}
}
public class Avion implements Volant {
@Override
public void voler() {
System.out.println("L'avion décolle.");
}
}
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();
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
public interface Payable {
double montant();
}
// ne compile pas
public class Facture implements Payable {
@Override
double montant() {
return 120.0;
}
}
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 :
public class Facture implements Payable {
@Override
public double montant() {
return 120.0;
}
}
public class Main {
public static void main(String[] args) {
Payable p = new Facture();
System.out.println("À payer : " + p.montant() + " €");
}
}
À payer : 120.0 €
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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é]
}
}
[Hugo, Inès, Léa, Noé]
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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()
}
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
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)";
}
}
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);
}
}
}
Le Petit Prince (96 pages)
Candide (180 pages)
Les Misérables (1500 pages)
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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()
}
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
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;
}
}
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);
}
}
[Camille, Sacha, Yanis]
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
@Override
public int compareTo(Joueur autre) {
return Integer.compare(autre.niveau, this.niveau); // inversé
}
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
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).
- Créez une liste
coursescontenantpain,laitetœufs. - Ajoutez
beurre, puis retirezlait. - Écrivez une méthode
static void afficher(List<String> liste)qui affiche chaque élément précédé de-, puis une dernière ligneTotal : N article(s). - Appelez cette méthode depuis
main.
Déroulé attendu :
- pain
- œufs
- beurre
Total : 3 article(s)
Votre code à compléter
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.
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);
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 21.4.B Trier avec un Comparator
Un Livre a un titre et un nombrePages.
- Ajoutez une méthode
toString()àLivre. - Écrivez deux « juges » :
ParTitre(ordre alphabétique des titres) etParPagesDecroissant(du plus épais au plus mince). - 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.
[Alice (160 p.), Dune (612 p.), Zadig (96 p.)]
[Dune (612 p.), Alice (160 p.), Zadig (96 p.)]
Votre code à compléter
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()
}
import java.util.Comparator;
public class ParTitre implements Comparator<Livre> {
// Méthode à écrire : public int compare(Livre a, Livre b)
}
import java.util.Comparator;
public class ParPagesDecroissant implements Comparator<Livre> {
// Méthode à écrire : public int compare(Livre a, Livre b)
}
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
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.)";
}
}
import java.util.Comparator;
public class ParTitre implements Comparator<Livre> {
@Override
public int compare(Livre a, Livre b) {
return a.getTitre().compareTo(b.getTitre());
}
}
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é
}
}
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);
}
}
[Alice (160 p.), Dune (612 p.), Zadig (96 p.)]
[Dune (612 p.), Alice (160 p.), Zadig (96 p.)]
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
- Écrivez une classe
Cafequi implémenteRunnable: sonrun()affichePréparer le café. - Écrivez une classe
Mailqui implémenteRunnable: sonrun()afficheLire les mails. - Dans
main, créez une listeList<Runnable> matincontenant unCafe, unMail, puis un deuxièmeCafe. - Parcourez la liste et déclenchez chaque action avec
run().
Déroulé attendu :
Préparer le café
Lire les mails
Préparer le café
Votre code à compléter
public class Cafe implements Runnable {
// Méthode à écrire : public void run()
}
public class Mail implements Runnable {
// Méthode à écrire : public void run()
}
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.
public class Cafe implements Runnable {
@Override
public void run() {
System.out.println("Préparer le café");
}
}
public class Mail implements Runnable {
@Override
public void run() {
System.out.println("Lire les mails");
}
}
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();
}
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 21
Quel mot-clé utilise une classe pour respecter une interface ?
extendssert à l'héritage de classe,implementsaux interfaces.Une classe peut-elle implémenter plusieurs interfaces ?
C'est un grand avantage par rapport à l'héritage de classe.
Que doit renvoyer
a.compareTo(b)siadoit passer avantb?Négatif = avant, 0 = égal, positif = après.
Pourquoi implémenter
Comparabledans une classe ?Sans
compareTo,Collections.sortne sait pas dans quel ordre ranger les objets.D'où vient l'interface
Comparable?Comme
String,Comparablefait partie dejava.lang.Quelle interface permet de trier les mêmes objets selon un autre critère, sans modifier leur classe ?
Comparatorest un « juge » extérieur, passé àCollections.sort(liste, juge).Quel est l'avantage d'écrire
List<String> noms = new ArrayList<>();plutôt queArrayList<String> noms = new ArrayList<>();?Le type à gauche dit ce qu'on peut faire, la classe à droite dit comment c'est fait.
Combien d'ordres de tri différents peut-on définir pour la classe
Joueuravec desComparator?Chaque critère (pseudo, niveau, niveau décroissant…) a son propre arbitre.
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 :
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
public interface Vendable {
// Méthode abstraite à déclarer : String getNom()
// Méthode abstraite à déclarer : int getPrix()
}
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()
}
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()
}
import java.util.Comparator;
public class ParPrix implements Comparator<Vendable> {
// Méthode à écrire : public int compare(Vendable a, Vendable b)
}
import java.util.Comparator;
public class ParNom implements Comparator<Vendable> {
// Méthode à écrire : public int compare(Vendable a, Vendable b)
}
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.
public interface Vendable {
String getNom();
int getPrix();
}
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 + " €)";
}
}
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 + " €)";
}
}
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());
}
}
import java.util.Comparator;
public class ParNom implements Comparator<Vendable> {
@Override
public int compare(Vendable a, Vendable b) {
return a.getNom().compareTo(b.getNom());
}
}
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 + " €");
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Partie 8 · 8 / 14
Les erreurs
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 ligneatest la plus utile. try/catchtryentoure le code risqué ;catchrattrape 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
Exceptionsans passer parRuntimeException: 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(ouRuntimeException) 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.
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 :
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 :
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]);
}
}
}
10
20
30
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
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 :
- quelle exception ?
- quelle ligne de votre code est en cause ?
- pourquoi ?
- comment corriger pour afficher
0quand le produit est inconnu ?
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"));
}
}
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
- Une
NullPointerException. Le message dit : Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.get(Object)" is null. - La première ligne
atqui vient de notre code désigneMain.prixDuProduit, ligne 7 :return catalogue.get(produit);. L'appel vient demain, ligne 17. "Éclair"n'est pas dans le catalogue :getrenvoienull. Or la méthode promet de renvoyer unint, et unintne peut pas êtrenull: Java tente de convertir lenullenint(méthodeintValue()) et lève laNullPointerException. (Voir la section 27.4.)- On utilise
getOrDefault, qui renvoie une valeur par défaut quand la clé est absente :
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"));
}
}
1
0
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
7
Le double de 7 est 14
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 22.4.B Redemander jusqu'à obtenir un nombre
Demandez un âge tant que la saisie n'est pas un nombre, puis affichez-le :
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
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
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
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).
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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 |
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());
}
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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
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
Bonbons : 17
Enfants : 5
Chacun reçoit 3 bonbons.
Il en reste 2.
Merci !
⌨ Valeurs à saisir, dans cet ordre
Bonbons : 17
Enfants : 0
Impossible de partager entre 0 enfant.
Merci !
⌨ Valeur à saisir
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 22.4.E Convertir des textes en nombres
Une liste de saisies arrive sous forme de textes : "12", "abc", "-5", "30", "7,5".
- Écrivez
static int convertir(String texte)qui renvoie l'entier correspondant, mais qui lève uneIllegalArgumentException(avec le messagenombre négatif : <texte>) si le nombre est négatif. - Dans
main, parcourez les cinq textes et affichez :"<texte>" → <nombre>si tout va bien ;"<texte>" → pas un nombrepour uneNumberFormatException;"<texte>" → refusé (<message>)pour uneIllegalArgumentException.
- 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
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
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);
}
}
"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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
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
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.
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);
}
}
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)
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Exercice 22.6.A Une carte cadeau qui refuse
Reprenez la CarteCadeau de l'exercice 15.3.A.
- Au lieu d'afficher « Solde insuffisant. »,
depenserlève uneIllegalArgumentExceptionavec un message. - Dans
main, dépensez 20 € puis 40 € sur une carte de 50 €, rattrapez l'exception et affichez son message :
Refusé : Solde insuffisant : 30.0 € restants
Solde : 30.0 €
Votre code à compléter
public class CarteCadeau {
// Attributs : solde
// Constructeur : CarteCadeau(double solde)
// Méthode à écrire : public double getSolde()
// Méthode à écrire : public void depenser(double montant)
}
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
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;
}
}
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() + " €");
}
}
Refusé : Solde insuffisant : 30.0 € restants
Solde : 30.0 €
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
ageest inférieur à 18 ; - sinon, elle affiche par exemple
Hugo est inscrit.
Dans main, inscrivez Hugo (20 ans) puis Léa (16 ans) :
Hugo est inscrit.
Inscription refusée. Léa a 16 ans : il faut avoir 18 ans.
Votre code à compléter
public class AgeInvalideException extends Exception {
// Constructeur : AgeInvalideException(String message)
}
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
public class AgeInvalideException extends Exception {
public AgeInvalideException(String message) {
super(message);
}
}
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());
}
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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 (avecthrows) simontantdé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
finallyafficheCarte rendue.à la fin.
Votre code à compléter
public class FondsInsuffisantsException extends Exception {
// Attributs : manque
// Constructeur : FondsInsuffisantsException(double manque)
// Méthode à écrire : public double getManque()
}
public class Distributeur {
// Attributs : solde
// Constructeur : Distributeur(double solde)
// Méthode à écrire : public void retirer(double montant) throws FondsInsuffisantsException
}
public class Main {
public static void main(String[] args) {
// Créez votre objet ici : distributeur
// Écrivez votre try / catch ici
}
}
Voir la solution
public class FondsInsuffisantsException extends Exception {
private double manque;
public FondsInsuffisantsException(double manque) {
super("Fonds insuffisants");
this.manque = manque;
}
public double getManque() {
return manque;
}
}
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 + " €");
}
}
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.");
}
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 22.9.A Suivre la propagation
Voici trois méthodes qui s'appellent en chaîne. Sans lancer le programme, répondez :
- qu'affiche le programme, ligne par ligne ?
- quelles lignes ne s'affichent jamais ?
- qui rattrape l'exception ?
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
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 :
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");
}
}
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)
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Exercice 22.12.A Chasse aux bugs au débogueur
Ce programme compile et ne plante pas, mais il affiche un résultat faux :
Articles : 4
Total : 4.0 €
Où poser les points d'arrêt et quel parcours suivre :
- 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. - Lancez avec Debug (l'insecte, ou Maj+F9). Le programme s'arrête sur la ligne 6, avant de l'exécuter.
- Bug 1. Dans le panneau Variables, lisez
i: il vaut 1 alors que ce premier tour devrait avoiri = 0, etnombrevaut 0. La case d'indice 0 (la quantité 2) ne sera jamais ajoutée. Regardez la lignefor: le départ de la boucle est faux. - 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. - Bug 2. Sur la ligne 14,
i = 0ettotal = 0.0. Faites un Step Over (F8) :totalvaut maintenant 5.0 (2.4 × 2). F9 pour le tour suivant :i = 1ettotal = 5.0. Nouveau F8 :totalvaut 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. - Arrêtez le débogueur (carré rouge), corrigez les deux lignes, relancez normalement : vous devez lire 6 articles et 14.5 €.
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 montrei = 1dès le premier tour.total:total = …remplace la valeur à chaque tour au lieu de l'ajouter. Au dernier tour,totalvaut 4.0 × 1.
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) + " €");
}
}
Articles : 6
Total : 13.5 €
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
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 :
- Posez un seul point d'arrêt, ligne 21 :
double port = fraisDePort(st);. Lancez avec l'insecte. - Dans le panneau Variables, vérifiez
st: vaut-il bien 50.0 ? - Step Into (
F7) pour entrer dansfraisDePort. Que vautsousTotal? - Avec Evaluate Expression (
Alt + F8), calculezsousTotal > 50. Quelle valeur ? - Step Over (
F8) : sur quelle ligne le débogueur s'arrête-t-il ? Qu'en concluez-vous ? Corrigez.
Voir la solution
- 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 danssousTotal. - Après Step Into, le débogueur est ligne 12 :
sousTotal = 50.0. sousTotal > 50vaut false : 50.0 n'est pas strictement supérieur à 50.- Step Over passe de la ligne 12 à la ligne 15 (
return 4.9;) : le blocreturn 0;est sauté. La condition>exclut 50 pile, alors qu'on voulait « à partir de 50 ». - Correction :
if (sousTotal >= 50).
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) + " €");
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 22
Que se passe-t-il si une exception n'est rattrapée nulle part ?
Elle remonte jusqu'à
main, puis le programme s'arrête. Le compilateur, lui, ne se soucie que des exceptions vérifiées.Dans une trace d'appels, par où commencer ?
La première ligne dit quoi et pourquoi ; la première ligne
atde votre code dit où regarder.Quel mot-clé lève une exception ?
throwlève l'exception ;throwsdéclare, dans la signature, ce que la méthode peut lever.Une exception vérifiée…
Le compilateur l'exige : must be caught or declared to be thrown.
Que vaut
10.0 / 0?Avec des
double, pas d'exception. Seule la division entière10 / 0lève uneArithmeticException.Votre point d'arrêt est posé, mais le programme ne s'arrête pas. Pourquoi ?
Avec la flèche verte, les points d'arrêt sont ignorés : il faut lancer en mode Debug.
Quel bloc s'exécute dans tous les cas, qu'une exception arrive ou non ?
finallysert au nettoyage : fermer un fichier, afficher « fin de l'opération »…mainappellea(), qui appelleb(), qui appellec().c()lève une exception que personne ne rattrape. Que se passe-t-il ?C'est la propagation : chaque méthode traversée est interrompue.
Quelle est la différence entre
throwetthrows?throw new …déclenche l'erreur,throws …prévient l'appelant.Pourquoi
10 / 0lève une exception alors que10.0 / 0donneInfinity?Les
doublesuivent la norme IEEE 754 :Infinity,-InfinityetNaNexistent.Un même
tryest suivi decatch (IllegalArgumentException e)et decatch (NumberFormatException e). Dans quel ordre faut-il les écrire ?NumberFormatExceptionhérite d'IllegalArgumentException: placée en second, elle ne serait jamais atteinte (erreur de compilation).Dans le débogueur, quelle action permet d'entrer dans la méthode appelée sur la ligne courante ?
Step Over exécute la ligne d'un coup, sans entrer dans la méthode.
Quelle erreur est détectée avant même de lancer le programme ?
Une erreur de compilation est repérée par IntelliJ (soulignement rouge) ; une exception n'apparaît qu'à l'exécution.
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 :
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é × prixet 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 :
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.
public class QuantiteInvalideException extends Exception {
public QuantiteInvalideException(String message) {
super(message);
}
}
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 + " €");
}
}
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Partie 9 · 9 / 14
Les objets en pratique
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
HashSetetHashMappour le ranger. Elle doit toujours accompagnerequals. - Contrat
equals/hashCode - Deux objets égaux selon
equalsdoivent avoir le mêmehashCode. record- Classe compacte, immuable, qui génère toute seule constructeur, accesseurs,
toString,equalsethashCode. - Constructeur compact
- Constructeur d'un
recordsans 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,equalsethashCode.
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
public class Produit {
// Attributs : nom, quantite
// Constructeur : Produit(String nom, int quantite)
// Méthode à écrire : public String toString()
}
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
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;
}
}
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);
}
}
Panier : [Baguette x2, Croissant x6]
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
- Écrivez
equalsethashCode. - Ajoutez trois clients à un
HashSet, dont deux avec le numéroC-001.
Le programme doit afficher Clients inscrits : 2.
Votre code à compléter
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()
}
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.
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();
}
}
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());
}
}
Clients inscrits : 2
Un seul attribut : numero.hashCode() suffit, Objects.hash(numero) marcherait aussi.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 23.3.B Les places du théâtre
Une place se repère par sa rangée (une lettre) et son numéro.
- Créez une classe
Place, avectoString,equalsethashCode. - Créez une
HashMap<Place, String>des réservations (place → nom du spectateur). - 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
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()
}
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.)
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);
}
}
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)));
}
}
Place B12 réservée par Léa
Place A1 libre : true
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
public record Note(String matiere, double valeur) {
// Méthode à écrire : public boolean estMoyenne()
}
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
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;
}
}
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());
}
}
}
Note[matiere=Maths, valeur=14.5] → moyenne : true
Refusée : Note hors de 0 à 20 : 25.0
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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 ?
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); }
}
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().
public record Coordonnees(double latitude, double longitude) {
}
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);
}
}
45.76
true
Coordonnees[latitude=45.76, longitude=4.84]
En plus : un toString lisible, que la classe n'avait pas.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 23
Que donne
System.out.println(livre)siLivrene redéfinit pastoString?C'est le
toStringhérité d'Object.Sans redéfinition, que compare
a.equals(b)?Le
equalsd'Objectrépondtrueseulement pour le même objet.Quelle signature redéfinit vraiment
equals?Avec
Livreen paramètre, c'est une surcharge, queHashSetn'appelle pas.Une classe redéfinit
equalsmais pashashCode. On ajoute deux objets égaux à unHashSet. Quelle taille ?Les deux objets ont des
hashCodedifférents : ils vont dans deux casiers, etequalsn'est pas appelé.Quelle règle est obligatoire ?
L'inverse n'est pas exigé : deux objets différents peuvent partager un casier.
Comment lit-on le titre de
record Livre(String titre, String auteur)?Les accesseurs d'un record portent le nom de l'attribut, sans
get. L'attribut lui-même est privé.Où vérifier qu'un prix n'est pas négatif dans un record ?
Le constructeur compact s'exécute avant que Java range les valeurs.
Pour un compte bancaire dont le solde change, on choisit plutôt…
Un record est immuable : il convient aux valeurs, pas aux objets qui changent.
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 :
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
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()
}
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.
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);
}
}
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);
}
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Extras · 10 / 14
POO avancée
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 :
Puce: un attributnumeroet son getter ;Chat, qui la compose : le chat crée sa puce dans son constructeur, et une méthodesePresenter()afficheChat <nom>, puce n°<numero>.
Votre code à compléter
public class Puce {
// Attributs : numero
// Constructeur : Puce(int numero)
// Méthode à écrire : public int getNumero()
}
public class Chat {
// Attributs : nom, puce
// Constructeur : Chat(String nom, int numeroPuce)
// Méthode à écrire : public void sePresenter()
}
public class Main {
public static void main(String[] args) {
// Créez vos objets ici
}
}
Voir la solution
public class Puce {
private int numero;
public Puce(int numero) {
this.numero = numero;
}
public int getNumero() {
return numero;
}
}
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());
}
}
public class Main {
public static void main(String[] args) {
new Chat("Félix", 4821).sePresenter();
}
}
Chat Félix, puce n°4821
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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 :
- un livre et ses chapitres ;
- une classe d'école et ses élèves ;
- un corps humain et son cœur ;
- une playlist et ses chansons.
Voir la solution
- Composition : sans le livre, ses chapitres n'ont pas de sens.
- Agrégation : les élèves existent même si la classe est dissoute.
- Composition : le cœur ne vit pas sans le corps (dans le modèle).
- Agrégation : les chansons existent en dehors de la playlist.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
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.
- Créez la classe
LigneFacture: trois attributs privésproduit(texte),quantite(entier) etprixUnitaire(décimal), un constructeur qui les reçoit, une méthodegetTotal()qui renvoiequantite * prixUnitaire, et untoString()qui renvoie par exemple3 x Cahier = 7.5 €. - Créez la classe
Facture: un attribut privénumero, un attributlignesde typeArrayList<LigneFacture>créé vide dès la déclaration, et un constructeur qui reçoit le numéro. AjoutezajouterLigne(String produit, int quantite, double prixUnitaire),getTotal()(la somme des totaux des lignes) etafficher(). - Dans
Main, créez la factureF-2026-014, ajoutez 3 cahiers à 2,5 € et 10 stylos à 1,2 €, puis affichez-la. - Question :
FactureetLigneFacturesont-elles liées par de la composition ou de l'agrégation ? Justifiez avec la règle « qui fait lenew? ».
Déroulé attendu :
Facture F-2026-014
3 x Cahier = 7.5 €
10 x Stylo = 12.0 €
Total : 19.5 €
Votre code à compléter
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()
}
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()
}
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.
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() + " €";
}
}
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() + " €");
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
public enum Saison {
// Écrivez votre code ici
}
public class Main {
public static void main(String[] args) {
// Parcourez avec une boucle for-each ici
}
}
Voir la solution
public enum Saison {
PRINTEMPS, ETE, AUTOMNE, HIVER
}
public class Main {
public static void main(String[] args) {
for (Saison s : Saison.values()) {
System.out.println(s);
}
}
}
PRINTEMPS
ETE
AUTOMNE
HIVER
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
- Écrivez un
enum NiveauLogavec quatre valeurs, chacune portant un entiergravite:DEBUG(0),INFO(1),WARN(2),ERROR(3). Ajoutez un gettergetGravite(). - Dans
Main, écrivez une méthodestatic void log(NiveauLog niveau, NiveauLog seuil, String message): elle affiche[NIVEAU] messageseulement si la gravité deniveauest supérieure ou égale à celle deseuil. - 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 :
[WARN] mot de passe faible
[ERROR] base injoignable
Votre code à compléter
public enum NiveauLog {
// Attributs : gravite
// Constructeur : NiveauLog(int gravite)
// Méthode à écrire : public int getGravite()
}
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.
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;
}
}
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");
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 24
Une voiture a un moteur. Quel outil utiliser ?
« A-un » se traduit par un attribut ; « est-un » par l'héritage.
Dans une composition, que devient la partie si le tout disparaît ?
En agrégation, au contraire, les parties peuvent exister seules.
Quel est l'intérêt d'un
enum?Le compilateur refuse toute valeur hors de la liste.
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 : leBuilder). - 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.
- Écrivez une interface
Paiementavec une méthode abstraitevoid payer(double montant)et une méthodedefaultvoid payerAvecRecu(double montant)qui appellepayer(montant)puis afficheReçu envoyé pour 25.0 €(avec le montant payé). - Écrivez
Carte(sonpayeraffichePaiement par carte : 25.0 €) etEspeces(sonpayeraffichePaiement en espèces : 25.0 €). Elles n'écrivent paspayerAvecRecu. - Écrivez
Cheque, dont lepayeraffichePaiement par chèque : 25.0 €, et qui redéfinitpayerAvecRecupour afficherPas de reçu pour un chèqueaprès avoir payé. - Dans
main, appelezpayerAvecRecu(25.0)sur uneCarte, desEspeceset unCheque.
Déroulé attendu :
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
public interface Paiement {
// Méthode abstraite à déclarer : void payer(double montant)
// Méthode à écrire : default void payerAvecRecu(double montant)
}
public class Carte implements Paiement {
// Méthode à écrire : public void payer(double montant)
}
public class Especes implements Paiement {
// Méthode à écrire : public void payer(double montant)
}
public class Cheque implements Paiement {
// Méthode à écrire : public void payer(double montant)
// Méthode à écrire : public void payerAvecRecu(double montant)
}
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.
public interface Paiement {
void payer(double montant);
default void payerAvecRecu(double montant) {
payer(montant);
System.out.println("Reçu envoyé pour " + montant + " €");
}
}
public class Carte implements Paiement {
@Override
public void payer(double montant) {
System.out.println("Paiement par carte : " + montant + " €");
}
}
public class Especes implements Paiement {
@Override
public void payer(double montant) {
System.out.println("Paiement en espèces : " + montant + " €");
}
}
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");
}
}
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);
}
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 25.2.A Une classe anonyme
Avec l'interface Notifiable (fichier Notifiable.java de la section 25.2, à avoir dans votre projet) :
- créez une classe anonyme qui affiche
Push :suivi du message ; - appelez
envoyerUrgent("Alerte").
Votre code à compléter
public class Main {
public static void main(String[] args) {
// Complétez ici
// Appelez la méthode envoyerUrgent() ici
}
}
Voir la solution
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
}
}
Push : URGENT : Alerte
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
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
public class Burger {
// Attributs : viande, fromage, salade
// Constructeur : Burger(Builder b)
// Méthode à écrire : public String toString()
}
public class Main {
public static void main(String[] args) {
// Complétez ici
// Affichez le résultat ici
}
}
Voir la solution
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);
}
}
}
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]
}
}
Burger bœuf [fromage=true, salade=false]
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 25
Que permet le mot-clé
defaultdans une interface ?La méthode est disponible tout de suite pour toutes les classes qui implémentent l'interface.
Quel est l'intérêt du pattern Builder ?
Chaque méthode renvoie
this, ce qui permet de chaîner les appels.
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 :
Détail de la commande :
Entrée : 6 €
Plat : 14 €
Dessert : 5 €
Total : 25 €
Statut : EN_PREPARATION
Votre code à compléter
public enum Statut {
// Écrivez votre code ici
}
public class Ligne {
// Attributs : plat, prix
// Constructeur : Ligne(String plat, int prix)
// Méthode à écrire : public String getPlat()
// Méthode à écrire : public int getPrix()
}
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()
}
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()
}
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>.
public enum Statut {
EN_PREPARATION, SERVIE, PAYEE
}
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;
}
}
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;
}
}
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);
}
}
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());
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Extras · 11 / 14
Le Java d'aujourd'hui
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 debreak, pas de cas qui « tombent » dans le suivant. - Switch expression
- Un
switchqui 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
switchou uninstanceofpour 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
public class Main {
public static void main(String[] args) {
// Écrivez votre boucle ici (for)
}
}
Voir la solution
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");
}
}
}
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
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 26.6.A Les formes, version moderne
Reprenez l'exercice 19.5.A, en version moderne :
- une interface
sealedForme; - deux records :
Rectangle(double largeur, double longueur)etCercle(double rayon); - une méthode
static double aire(Forme f)avec unswitchsur le type, sansdefault.
Affichez chaque forme et son aire.
Votre code à compléter
public record Rectangle(double largeur, double longueur) implements Forme {
// Écrivez votre code ici
}
public record Cercle(double rayon) implements Forme {
// Écrivez votre code ici
}
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
public sealed interface Forme permits Rectangle, Cercle {
}
public record Rectangle(double largeur, double longueur) implements Forme {
}
public record Cercle(double rayon) implements Forme {
}
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));
}
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 26
Quel est le type de
nomaprèsvar nom = "Léa";?Le type est deviné à la compilation, une fois pour toutes.
Laquelle de ces lignes compile ?
vardemande une valeur dont on peut déduire le type, et seulement dans une méthode.Avec
case 6, 7 -> …, faut-il unbreak?La flèche exécute seulement ce qui la suit.
Dans un
switchexpression, à quoi sertyield?Un cas sur une ligne renvoie sa valeur directement ; un bloc a besoin de
yield.Que permet
if (objet instanceof String s)?C'est un motif : le test et la variable en une fois.
Que fait
sealed interface Paiement permits Carte, Especes?La liste des classes autorisées est fermée et connue du compilateur.
Un
switchsur une interfacesealedoublie un cas. Que se passe-t-il ?Le compilateur connaît tous les cas : « the switch expression does not cover all possible input values ».
Dans un
switchsur des types, que faitcase CarteCadeau cc when cc.solde() < montant?whenest une garde ; les cas sont testés dans l'ordre.
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 :
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
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.
public sealed interface Paiement permits Carte, Especes, Cheque {
double montant();
}
public record Carte(double montant) implements Paiement {}
public record Especes(double montant) implements Paiement {}
public record Cheque(double montant) implements Paiement {}
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));
}
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Extras · 12 / 14
Passer de PHP à Java
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.
==etequals- Pour les types primitifs,
==compare les valeurs. Pour les objets (dontString),==compare les références : on utiliseequalspour comparer le contenu. - Division entière
- Entre deux
int,/donne un entier et supprime la partie décimale :7 / 2vaut3. - 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.
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);
}
}
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) :
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);
}
}
Commande : [croissant]
Grande commande : [croissant, café]
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
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");
}
}
}
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 :
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");
}
}
}
Égalité !
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
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));
}
}
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 :
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));
}
}
Moyenne : 14.333333333333334
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
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);
}
}
}
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 :
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);
}
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
- Écrivez
static Integer chercherAge(Map<String, Integer> annuaire, String prenom)qui renvoie l'âge, ounullsi le prénom est inconnu. - Dans
main, cherchezLéapuisZoé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
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
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");
}
}
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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 :
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"));
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 27
En Java, une méthode reçoit une
ArrayListet lui ajoute un élément. Que voit l'appelant ?La méthode reçoit une copie de la référence, qui désigne la même liste.
Integer a = 500; Integer b = 500;Que vauta == b?Hors de -128 à 127, chaque
Integerest un objet distinct. On compare avecequals.Que vaut
"5" + 3en Java ?Dès qu'un des deux côtés est un texte,
+concatène. Pour additionner :Integer.parseInt("5") + 3.Que se passe-t-il avec
int n = Integer.MAX_VALUE + 1;?Un
intqui dépasse « fait le tour ». Pour de plus grands nombres :long.List<String> panier = new ArrayList<>();Que faitif (panier) { … }?Java n'a pas de valeurs « vraies » ou « fausses ». On écrit
!panier.isEmpty().Quel est l'équivalent de
$ville ?? "inconnue"?Java n'a pas d'opérateur pour
null: on utilise un test ou cette méthode dejava.util.Objects.Deux méthodes
afficher(int x)etafficher(double x). Que faitafficher(7 / 2)?7 / 2est une division entière, donc unint: le compilateur choisit la versionint.Un fichier
Main.javacontientpublic class Mainetpublic class Client. Que se passe-t-il ?Une classe
publicvit dans un fichier qui porte exactement son nom.Peut-on écrire
int age = null;?nullsignifie « aucun objet » : il n'a de sens que pour un type référence (Integer,String…).Que se passe-t-il avec
Integer a = null; int b = a;?Java essaie de retirer l'enveloppe d'un objet qui n'existe pas.
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.
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
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);
}
}
Rôle : admin
Accès autorisé
Moyenne : 22.5
Notes : [12, 15, 20]
Ville : inconnue
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Extras · 13 / 14
Les outils du quotidien
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.ofouMap.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
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 :
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());
}
}
}
le : 3
chat : 2
voit : 1
chien : 1
et : 1
fuit : 1
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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.
- Affichez-le trié par prénom, une ligne par personne.
- Cherchez ensuite
Inèset affichez son numéro, ou un message si elle n'y est pas.
Votre code à compléter
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
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");
}
}
}
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
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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".
- Rangez les quantités dans une
Map<String, Integer>, en additionnant les livraisons d'un même produit (avecgetOrDefault). - 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é.
cahier : 10
gomme : 3
stylo : 15
Votre code à compléter
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
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());
}
}
}
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é.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 28
Dans quel ordre une
HashMapaffiche-t-elle ses clés ?Pour l'ordre d'ajout,
LinkedHashMap; pour un ordre trié,TreeMap.Quelle collection Java se comporte comme un tableau associatif PHP ?
Elle associe des clés à des valeurs et garde l'ordre d'ajout, comme le tableau PHP.
Que rend
stock.getOrDefault("Flan", 0)si la clé"Flan"n'existe pas ?C'est tout son intérêt :
getaurait rendunull.On ajoute un objet d'une classe qui n'implémente pas
Comparabledans unTreeSetsansComparator. Que se passe-t-il ?Le
TreeSetdoit comparer ses éléments : sanscompareTo, il ne peut pas.List<String> jours = List.of("Lundi"); jours.add("Mardi");donne…List.ofcrée une liste immuable. Pour la modifier :new ArrayList<>(List.of(…)).
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) :
["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 :
Ventes par produit :
baguette : 3
croissant : 2
pain : 1
Best-seller : baguette (3 vendus)
Votre code à compléter
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.
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)");
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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). PathetFiles- 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 :
- son âge sous la forme
36 ans, 3 mois et 4 jours(avecPeriod.between) ; - 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()).
36 ans, 3 mois et 4 jours
13245 jours vécus
Votre code à compléter
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
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");
}
}
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é.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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 :
- la date limite, au format français (
dd/MM/yyyy) ; - le nombre de jours restants, ou le nombre de jours de retard.
Votre code à compléter
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
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");
}
}
}
À 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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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).
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
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
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);
}
}
}
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)
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 29.2.D La durée d'un trajet
Un train part le 19 octobre 2026 à 08h45 et arrive à 11h20. Calculez et affichez :
- la durée en minutes (
ChronoUnit.MINUTES.betweensur deuxLocalDateTime) ; - cette durée sous la forme
2 h 35; - l'heure d'arrivée si le train a 25 minutes de retard (
plusMinutes), au formatHH:mm.
Durée : 2 h 35
Arrivée avec retard : 11:45
Votre code à compléter
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
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));
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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
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).
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));
}
}
[Léa, Hugo, Inès]
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 29.4.B Le journal de bord
Dans un fichier journal.txt :
- écrivez trois lignes :
Départ,Escale à Lisbonne,Tempête; - ajoutez une quatrième ligne,
Arrivée, à la fin du fichier, sans effacer les trois premières (optionStandardOpenOption.APPENDdeFiles.writeString) ; - relisez le fichier avec
Files.readAllLineset affichez chaque ligne numérotée (1. Départ…), puis le nombre de lignes ; - supprimez le fichier (
Files.deleteIfExists).
Votre code à compléter
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
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);
}
}
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).
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Exercice 29.4.C Le fichier introuvable
Lisez le fichier inexistant.txt avec Files.readAllLines, avec trois blocs :
- d'abord
catch (NoSuchFileException e): le fichier n'existe pas ; affichezFichier introuvable : <nom>avece.getMessage(); - puis
catch (IOException e): toute autre erreur de lecture ; affichezLecture impossible.; - un
finallyqui afficheFin de la lecture.
Pourquoi faut-il écrire NoSuchFileException avant IOException ?
Votre code à compléter
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
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.");
}
}
}
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).
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
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 :
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
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 :
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() + " €");
}
}
}
Hugo : 11.0 €
Inès : 20.0 €
Léa : 17.0 €
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
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.
- Écrivez-le (avec
Files.write(chemin, liste)), puis relisez-le. - En ignorant l'en-tête, calculez la moyenne des notes valides et trouvez le meilleur élève.
- Une note qui n'est pas un nombre (
Noé) est ignorée avec le messageLigne ignorée : Noé;abc. - Affichez la moyenne et le meilleur élève, puis supprimez le fichier.
Votre code à compléter
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
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);
}
}
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.
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Testez-vous : QCM
QCM · Chapitre 29
Quel type choisir pour l'heure d'ouverture d'une boutique ?
Une heure seule, sans date.
Quel motif lit la date
14/07/2026?MMest le mois ;mmdésigne les minutes.Comment obtenir le nombre de jours entre deux dates
aetb?Period.getDays()ne rend que la partie « jours » de la période, sans les mois ni les années.Pourquoi
Files.readString(chemin)ne compile-t-il pas seul dans unmainsansthrowsnitry?Une exception vérifiée doit être rattrapée ou déclarée avec
throws.Que rend
readLine()d'unBufferedReaderà la fin du fichier ?D'où la boucle
while ((ligne = lecteur.readLine()) != null).Files.writeString(chemin, "Œufs\n")sur un fichier qui existe déjà…Par défaut, l'écriture remplace le contenu.
Comment ajouter une ligne à la fin d'un fichier sans effacer son contenu ?
Sans
APPEND, l'écriture remplace tout le contenu du fichier.Dans un
tryqui lit un fichier, quelcatchfaut-il écrire en premier ?NoSuchFileExceptionhérite d'IOException: le cas précis doit précéder le cas général.
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 :
4 ventes, total : 23,20 €
De 2026-01-10 à 2026-01-15 (5 jours)
Votre code à compléter
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.
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)");
}
}
Vérifiable : cet exercice a un test automatique. Dans IntelliJ, ouvrez tests > le chapitre > l'exercice > MainTest, puis cliquez sur le triangle vert : une coche verte signifie que votre programme affiche bien le résultat attendu.
Extras · 14 / 14
Le projet final
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| Étape | Ce qu'on construit | Ce qu'on réutilise |
|---|---|---|
| 1 · Le modèle | Achat, Niveau, Client | classes et encapsulation (13, 14), enum (24.2), record, equals et hashCode (22), switch moderne (25), LocalDate (27) |
| 2 · Les règles | Programme et deux exceptions | les maps (11, 26), les constantes (15), les exceptions vérifiées (19) |
| 3 · Le menu | Caisse et la boucle du Main | Scanner (6), les boucles (9), try / catch (19), switch moderne (25) |
| 4 · Sauvegarder (facultatif) | Sauvegarde et le fichier clients.csv | les 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 uneIllegalArgumentException, dès la création.Niveau: un enumBRONZE,ARGENT,OR, avec une méthodestatic Niveau depuis(int cumul)et une méthodeint multiplicateur()écrite avec unswitchmoderne.Client: un numéro et un nom qui ne changent plus, des points disponibles et des points cumulés. Une méthodeint 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.toStringafficheC001 · Léa · 45 points · BRONZE.
Écrivez Achat, Niveau et Client. Dans un Main :
- 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 ;
- vérifiez qu'un
HashSetne garde qu'un seul client quand on y ajoute deux objetsClientavec le numéroC001; - vérifiez qu'un achat de -3 € est refusé.
Voir la solution
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);
}
}
}
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;
};
}
}
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());
}
}
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));
}
}
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.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
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 uneClientInconnuExceptionsi 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 unePointsInsuffisantsException;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 :
- inscrivez Léa et Hugo ;
- Léa achète pour 230,40 €, Hugo pour 12,50 € ;
- Léa prend une viennoiserie, puis Hugo essaie aussi ;
- essayez un achat sur la carte
C099; - affichez tous les clients.
Voir la solution
public class ClientInconnuException extends Exception {
public ClientInconnuException(String numero) {
super("client inconnu : " + numero);
}
}
public class PointsInsuffisantsException extends Exception {
public PointsInsuffisantsException(int points) {
super("points insuffisants (" + points + " sur " + Programme.PRIX_RECOMPENSE + ")");
}
}
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
}
}
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);
}
}
}
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.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
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 :
- inscrire Léa ;
- un achat de 45,80 € ;
- un montant « trois euros » ;
- un achat pour la carte
C002qui n'existe pas ; - une récompense trop tôt ;
- le choix 9 ;
- la liste des clients, puis quitter.
Voir la solution
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);
}
}
}
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
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.
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
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 ;Programmegagne une méthodereprendre(List<Client> anciens)qui remet ces clients dans la map ;- une
IOExceptionne 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 :
- première exécution : inscrivez Léa, un achat de 180 €, quittez ;
- ouvrez
clients.csv; - relancez : Léa doit être toujours là.
Voir la solution
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;
}
}
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
}
}
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
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
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 !
Non vérifiable : pas de test automatique pour cet exercice (réponse libre, observation, saisie au clavier ou résultat qui change à chaque lancement). Comparez avec la solution une fois l'exercice terminé, ou montrez-le à votre formateur.
Testez-vous : QCM
QCM · Chapitre 30
int[] a = {1, 2}; int[] b = a; b[0] = 9;Que vauta[0]?En Java, une variable de tableau ou d'objet contient une référence :
b = ane copie rien. En PHP, le tableau aurait été copié.Une classe redéfinit
equalsmais pashashCode. On ajoute à unHashSetdeux objets égaux selonequals. Que donnesize()?Deux objets égaux doivent avoir le même
hashCode. Sans cela, leHashSetne les compare même pas avecequals.Que ne fournit pas le record
record Achat(double montant, LocalDate date)?Un record est non modifiable : ses champs sont
final, il n'a pas de setter.Dans
Niveau, leswitch (this) { case BRONZE, ARGENT -> 1; case OR -> 2; }n'a pas dedefault. Pourquoi compile-t-il ?Un
switchqui renvoie une valeur doit être exhaustif. Avec un enum, lister toutes les constantes suffit ; si on en ajoute une, le compilateur signale leswitchà compléter.sealed interface Recompense permits Viennoiserie, Remise {}. Que se passe-t-il si une classeCafeécritimplements Recompense?sealedfixe la liste complète des implémentations : c'est ce qui permet auswitchd'être exhaustif sansdefault.Pourquoi
Programmerange-t-il ses clients dans uneLinkedHashMapplutôt qu'uneHashMap?Une
HashMapne garantit aucun ordre. Celle qui trie ses clés, c'est laTreeMap.LocalDate d = LocalDate.of(2026, 10, 23); d.plusDays(1); System.out.println(d);Qu'affiche ce code ?Comme
String,LocalDateest non modifiable :plusDaysrenvoie une nouvelle date, ici perdue. Il fallait écrired = d.plusDays(1);.On appelle
Files.readAllLines(fichier)dans une méthode, sanstry/catchnithrows. Que se passe-t-il ?Une exception vérifiée doit être rattrapée ou déclarée avec
throws. Le compilateur l'exige.
Java Avancé · Partie 1 · 1 / 2
Introduction
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.
<?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.
<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.
<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.
<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.
<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
Quel mot-clé rend un attribut accessible uniquement à l'intérieur de sa classe ?
Chapitre 15 : un attribut
privaten'est visible que dans sa classe. On passe par des méthodes pour le lire ou le modifier.Quelle classe permet de stocker une liste qui peut grandir ou rétrécir ?
Chapitre 11 :
ArrayListest une liste dont la taille s'adapte. Un tableauint[]a une taille fixe.Quelle boucle parcourt tous les éléments d'une liste sans gérer d'indice ?
Chapitre 9 : la boucle « pour chaque » (
for (String s : liste)) donne directement chaque élément, sans indice. Si vous avez besoin de l'indice (position 0, 1, 2…), il faut la boucle classiquefor (int i = 0; i < n; i++), où le compteuriest créé puis augmenté à chaque tour.Quel bloc permet de rattraper une exception ?
Chapitre 22 :
trycontient le code qui peut échouer. Si une exception est levée dans ce bloc, Java saute directement aucatchcorrespondant, qui décide quoi faire (message, valeur par défaut, relance). Sanscatch, l'exception remonte et arrête le programme.Quel mot-clé lève volontairement une exception ?
Chapitre 22 :
throw new ...crée une exception et la déclenche à cet endroit : l'exécution de la méthode s'arrête et Java cherche uncatchplus haut dans la pile d'appels.throwlance l'exception ;catchla rattrape.Quel bloc ferme automatiquement une ressource, comme une connexion à la base ?
Chapitre 22 : les parenthèses après
try(try (Connection c = ...)) déclarent une ressource qui doit être fermée (connexion, fichier…). Java appelle automatiquement sa méthodeclose()à la fin du bloc, même si une erreur s'est produite. Sans cette syntaxe, il faudrait penser soi-même à la fermer dans unfinally, ce qui est facile à oublier.Quel mot-clé permet à une classe d'implémenter une interface ?
Chapitre 21 :
implementspour une interface,extendspour l'héritage de classe.
À 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
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
Chef chef = new Chef("Mamie Jeanne", 40); // new + constructeur généré
System.out.println(chef); // toString() généré
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) {}
Chef[nom=Mamie Jeanne, annees=40]
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
package com.example.demo;
// Trois valeurs fixes, séparées par des virgules
public enum Difficulte { FACILE, MOYEN, DIFFICILE }
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());
public record Recette(String nom, Categorie categorie, int minutes, boolean vegetarienne, Difficulte difficulte) {}
Testez-vous : QCM
QCM · Chapitre 32
Dans quel fichier déclare-t-on les dépendances d'un projet Maven ?
Le
pom.xmlest le fichier de configuration de Maven.Quel est le rôle de Maven dans le projet ?
Maven lit le pom.xml, télécharge les dépendances et compile le projet. Il ne stocke pas de données et ne remplace pas le compilateur.
Quelles sont les trois coordonnées d'une dépendance ?
Elles identifient la bibliothèque de façon unique.
À 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).
9
Votre code à compléter
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
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)));
}
}
Exercice 33.B Un test de positivitéEssentiel
Créez un Predicate<Integer> nommé estPositif. Testez-le avec -3 puis avec 5.
false
true
Votre code à compléter
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
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));
}
}
Testez-vous : QCM
QCM · Chapitre 33
Que signifie
TdansBoite<T>?Test un paramètre de type : on écritBoite<String>pour le remplacer.Pourquoi écrit-on
List<Integer>et nonList<int>?Les types primitifs (
int,double…) ne sont pas des objets.Integerest sa version objet.Quelle écriture déclare une classe générique ?
Le paramètre de type est écrit entre chevrons, après le nom de la classe.
Quelle interface utiliser pour une fonction qui reçoit un nombre et renvoie un texte ?
Function<Integer, String>transforme une valeur d'un type en une valeur d'un autre.Qu'est-ce que
x -> x * 2en Java ?La flèche
->sépare les paramètres du corps de la fonction.
À 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).
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.
[ALI, LÉA, SAMI]
Votre code à compléter
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
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);
}
}
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).
Optional[4]
Optional.empty
Votre code à compléter
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
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)));
}
}
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
long dispo = dao.lister().stream().filter(Media::available).count();
System.out.println(dispo);2Exercice 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
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)
}
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
trouverParAuteurdans l'interfaceMediaDao.
Pourquoi : Une méthode ajoutée au contrat doit être implémentée partout : c'est pour cela que l'interface change en premier.
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
trouverParAuteuravec unfiltersurauthor, puistoList().
Pourquoi : Sans cette méthode, la classe ne compile plus : elle doit tenir la promesse de l'interface.
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.
Testez-vous : QCM
QCM · Chapitre 34
Que fait
filterdans un Stream ?filterprend unPredicateet ne garde que les éléments qui renvoient vrai.Quelle opération termine un Stream et renvoie un résultat ?
Les opérations terminales produisent le résultat final.
Que fait
orElse("Inconnu")sur unOptionalvide ?orElsefournit une valeur de secours quand la boîte est vide.Quel est le problème principal de
null?Utiliser une valeur null comme un objet provoque l'erreur.
À 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) :
java -cp ~/.m2/repository/com/h2database/h2/2.3.232/h2-2.3.232.jar org.h2.tools.ConsoleSous Windows PowerShell, remplacez ~ par $HOME. Un navigateur s'ouvre sur http://localhost:8082.
2. Se connecter à la base du projet :
| Champ | Valeur à saisir |
|---|---|
| Saved Settings | Generic H2 (Embedded) |
| JDBC URL | jdbc:h2:file:/chemin/absolu/vers/mediatheque (sans l'extension .mv.db) |
| User Name | vide |
| Password | vide |
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) :
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 :
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
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
String sql = "SELECT COUNT(*) FROM media";COUNT(*) renvoie une seule ligne avec une seule colonne : le nombre de lignes de la table.rs.next();On se place sur l'unique ligne.return rs.getInt(1);Première (et seule) colonne.
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
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);
}
}
Testez-vous : QCM
QCM · Chapitre 36
Que représente le
?dans une requête préparée ?Le
?est remplacé par une valeur sûre, jamais interprétée comme du SQL.Pourquoi
PreparedStatementprotège-t-il contre l'injection ?La requête est fixée à l'avance, les valeurs arrivent ensuite comme des données.
Quel est le risque principal d'une requête construite par concaténation ?
Une saisie peut modifier la requête si elle est collée dans le SQL.
Quel est le numéro du premier
?dans unPreparedStatement?Les paramètres sont numérotés à partir de 1.
À 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.
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
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);
}
}
true // changerDuree(1, 14) : le média 1 existe
false // changerDuree(999, 14) : aucune ligne modifiée
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
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);
}
}
1 // premier appel : le média existait
0 // second appel : déjà supprimé
Testez-vous : QCM
QCM · Chapitre 37
Que fait
rollback()?rollbackannule tout ce qui a été fait depuis le début de la transaction.Qu'apporte une transaction ?
On valide avec
commit(), ou on annule avecrollback().Que signifie DAO ?
Un DAO est l'objet qui donne accès aux données.
Quel est l'intérêt principal d'une interface DAO ?
Le reste du programme ne dépend que du contrat, pas de l'implémentation.
À 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
// 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");
}
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
// 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
public List<Media> disponibles() {Le service combine les données du dépôt : le dépôt, lui, n'a pas changé.return medias.lister().stream()Stream : parcours de la liste..filter(Media::available)On garde les médias disponibles..toList();On reforme une liste.
Testez-vous : QCM
QCM · Chapitre 38
Quel code HTTP Spring renverra-t-il, plus tard, pour l'ajout d'un titre déjà existant ?
Le service lève
IllegalStateException, que Spring traduira en 409 Conflict.Dans
new MediaService(medias, loans), que fait-on ?Le service reçoit sa dépendance de l'extérieur au lieu de la fabriquer : c'est l'injection de dépendances.
Quel est l'intérêt d'avoir le champ du service de type
MediaDao(l'interface) plutôt queMediaDaoJdbc?Le service ne dépend que du contrat : n'importe quelle implémentation (mémoire, JDBC, faux dépôt de test) peut lui être donnée.
Où doivent se trouver les règles métier (« titre obligatoire », quota de 5 emprunts) ?
Le service les porte. Un front peut les vérifier aussi pour l'ergonomie, mais le serveur ne doit jamais lui faire confiance.
Quelle exception correspond à un titre déjà existant, selon notre convention ?
Les données sont valides, mais l'état actuel (le titre existe déjà) interdit l'opération : conflit, 409.
Pourquoi écrire
log.info("Ajout de {}", titre)plutôt quelog.info("Ajout de " + titre)?SLF4J remplace les
{}seulement si le message est réellement écrit ; la concaténation est faite à chaque appel.Quel est le rôle d'une interface DAO ?
L'interface est le contrat ; l'implémentation fait le travail.
Pourquoi le reste du programme ne doit-il pas connaître JDBC ?
Il n'utilise que l'interface : la version mémoire et la version JDBC sont interchangeables.
À 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
@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));
}
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
@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());
}
Testez-vous : QCM
QCM · Chapitre 39
Pourquoi un test du service peut-il s'exécuter sans base de données ?
Le service ne dépend que de l'interface du dépôt : le test lui donne une implémentation en mémoire.
Que fait
verify(dao, never()).ajouter(any())?verifycontrôle, après coup, les appels reçus par un faux objet Mockito.Que signifie
jdbc:h2:mem:test?mem:: la base n'existe que dans la mémoire du programme, idéale pour les tests.Quelle annotation marque une méthode comme test en JUnit 5 ?
@Testest importée depuisorg.junit.jupiter.api.À quoi sert
assertThrows?Le test réussit si le code fourni lève l'exception attendue.
Quelle assertion vérifie qu'une exception est bien levée ?
Elle réussit si l'exception attendue est 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.