■ Java
Concepts &
Développeurs
Avancé
Patterns pour
Suite du guide — Niveau intermédiaire / avancé
Generics · Collections · Streams · Lambdas · Concurrence · Design Patterns
10 chapitres · Volume 2
Table des matières
11. Generics (Types génériques) p. 3
12. Collections — List, Set, Map p. 3
13. Itérateurs & for-each avancé p. 4
14. Lambdas & Interfaces fonctionnelles p. 5
15. Streams API p. 6
16. Optional — Éviter NullPointerException p. 7
17. Concurrence & Threads p. 7
18. Design Patterns essentiels p. 8
19. Entrées / Sorties (I/O & NIO) p. 9
20. Bonnes pratiques & Java moderne p. 10
11. Generics (Types génériques)
Les generics permettent d'écrire du code réutilisable et type-safe sans duplication. On utilise des
paramètres de type entre chevrons <T>.
// Classe générique public class Boite { private T contenu; public Boite(T contenu) {
[Link] = contenu; } public T getContenu() { return contenu; } } Boite s = new
Boite<>("Bonjour"); Boite n = new Boite<>(42); [Link]([Link]()); //
Bonjour [Link]([Link]()); // 42 // Méthode générique public static >
T max(T a, T b) { return [Link](b) >= 0 ? a : b; } [Link](max(10,
25)); // 25 [Link](max("chat","zoo")); // zoo
■ Wildcard : <?> = type inconnu, <? extends X> = sous-type de X, <? super X> = super-type de X.
12. Collections — List, Set, Map
Le Java Collections Framework fournit des structures de données dynamiques et performantes,
toutes génériques.
import [Link].*; // LIST — éléments ordonnés, doublons autorisés List liste = new
ArrayList<>(); [Link]("Alice"); [Link]("Bob"); [Link]("Alice");
[Link]([Link]()); // 3 [Link]([Link](0)); // Alice
[Link]("Bob"); // SET — pas de doublons Set set = new HashSet<>([Link](1,
2, 2, 3)); [Link](set); // [1, 2, 3] // MAP — clé → valeur Map scores =
new HashMap<>(); [Link]("Alice", 95); [Link]("Bob", 80);
[Link]([Link]("Alice")); // 95 [Link]("Eve", 0); // 0 si
absent
13. Itérateurs & for-each avancé
Toute collection implémentant Iterable peut être parcourue avec for-each ou un Iterator explicite
(utile pour supprimer des éléments en cours de boucle).
List noms = new ArrayList<>([Link]("Alice","Bob","Charlie","Dave")); //
Iterator explicite — suppression sécurisée Iterator it = [Link](); while
([Link]()) { String nom = [Link](); if ([Link]("C")) { [Link](); //
sûr contrairement à [Link]() dans for-each } } [Link](noms); //
[Alice, Bob, Dave]
Parcourir une Map
Map ages = [Link]("Alice", 30, "Bob", 25, "Charlie", 35); // Parcours des entrées for
([Link] e : [Link]()) { [Link]([Link]() + " → " +
[Link]()); } // Méthode forEach (Java 8+) [Link]((nom, age) ->
[Link](nom + " a " + age + " ans"));
Collections utilitaires
import [Link]; List nums = new ArrayList<>([Link](5, 2, 8, 1,
9)); [Link](nums); // [1, 2, 5, 8, 9] [Link](nums); // [9, 8,
5, 2, 1] [Link](nums); // ordre aléatoire int min =
[Link](nums); int max = [Link](nums); // Trier avec Comparator
[Link]([Link]()); // Trier une liste d'objets par champ List mots
= [Link]("banane","abricot","cerise");
[Link]([Link](String::length)); [Link](mots); //
[banane, cerise, abricot]
■■ ConcurrentModificationException : Ne jamais modifier une collection avec [Link]() à l'intérieur
d'une boucle for-each — utiliser un Iterator ou un Stream avec filter.
14. Lambdas & Interfaces fonctionnelles
Introduites en Java 8, les expressions lambda sont des fonctions anonymes concises. Elles
implémentent une interface fonctionnelle (interface avec une seule méthode abstraite).
Syntaxe lambda
// Forme complète (int a, int b) -> { return a + b; } // Forme abrégée (types inférés,
return implicite) (a, b) -> a + b // Sans paramètre () -> [Link]("Hello
!") // Un seul paramètre (parenthèses optionnelles) x -> x * x
Interfaces fonctionnelles du package [Link]
Interface Signature Usage typique
Runnable () → void Thread, tâche sans retour
Supplier<T> () → T Fournir une valeur
Consumer<T> T → void Consommer/afficher
Function<T,R> T→R Transformer T en R
Predicate<T> T → boolean Filtrer / tester
BiFunction<T,U,R> (T, U) → R Deux entrées, un résultat
Exemples concrets
import [Link].*; Predicate estLong = s -> [Link]() > 5;
[Link]([Link]("Bonjour")); // true
[Link]([Link]("Hi")); // false Function longueur = String::length;
// référence de méthode [Link]([Link]("Java")); // 4 Consumer
afficher = [Link]::println; [Link]("Hello Lambda !"); Supplier aleatoire
= Math::random; [Link]([Link]()); // ex: 0.7342...
15. Streams API
L'API Stream (Java 8+) permet de traiter des collections de manière fonctionnelle, déclarative et
chainable. Un Stream ne stocke pas de données — il les traite à la volée.
Pipeline Stream : source → opérations intermédiaires → opération terminale
import [Link].*; List noms =
[Link]("Alice","Bob","Charlie","David","Eve","Anna"); // Exemple complet List
resultat = [Link]() .filter(n -> [Link]("A")) // intermédiaire : filtre
.map(String::toUpperCase) // intermédiaire : transformation .sorted() // intermédiaire
: tri .collect([Link]()); // terminale : collecte
[Link](resultat); // [ALICE, ANNA]
Opérations intermédiaires (lazy)
List nombres = [Link](1, 2, 3, 4, 5, 6, 7, 8, 9, 10); [Link]() .filter(n -> n
% 2 == 0) // garde les pairs .map(n -> n * n) // met au carré .limit(3) // prend les 3
premiers .forEach([Link]::println); // 4, 16, 36
Opérations terminales
List nums = [Link](3, 1, 4, 1, 5, 9, 2, 6); long count = [Link]().filter(n -> n
> 3).count(); // 4 int sum = [Link]().mapToInt(Integer::intValue).sum(); // 31
int max = [Link]().mapToInt(x->x).max().getAsInt(); // 9 double avg =
[Link]().mapToInt(x->x).average().getAsDouble(); // 3.875 // Réduction
personnalisée int produit = [Link]().reduce(1, (a, b) -> a * b); // 6480 //
Groupement Map> parMod3 = [Link]() .collect([Link](n -> n % 3));
// {0=[3,9,6], 1=[1,1,4], 2=[2,5]} // Jointure de chaînes String joined =
[Link]().collect([Link](", ")); // "Alice, Bob, Charlie, ..."
16. Optional — Éviter NullPointerException
Optional<T> est un conteneur qui peut contenir ou non une valeur. Il force à gérer explicitement les
cas null, rendant le code plus sûr et expressif.
import [Link]; // Créer un Optional Optional present =
[Link]("Java"); Optional vide = [Link](); Optional nullable =
[Link](null); // safe // Vérifier et obtenir
[Link]([Link]()); // true [Link]([Link]());
// true [Link]([Link]()); // Java // Valeur par défaut String val =
[Link]("Défaut"); // "Défaut" String val2 = [Link](() -> "Calculé"); //
Chaînage fonctionnel Optional longueur = present .filter(s -> [Link]() > 2)
.map(String::length); [Link](l -> [Link]("Longueur : " + l));
// 4 // orElseThrow String result = [Link](() -> new
IllegalStateException("Valeur absente !"));
17. Concurrence & Threads
Java supporte nativement le multithreading via la classe Thread, l'interface Runnable, et depuis
Java 5 le package [Link].
// 1. Créer un thread avec Runnable (lambda) Thread t1 = new Thread(() -> { for (int i
= 0; i < 3; i++) { [Link]("Thread 1 - étape " + i); } }); [Link](); //
2. ExecutorService — pool de threads (recommandé) import [Link].*;
ExecutorService pool = [Link](3); [Link](() ->
[Link]("Tâche A dans " + [Link]().getName()));
[Link](() -> [Link]("Tâche B")); [Link](); // 3. Future —
résultat asynchrone Future future = [Link](() -> { [Link](100); return 42;
}); [Link]([Link]()); // bloque jusqu'au résultat : 42 // 4.
synchronized — section critique private int compteur = 0; public synchronized void
incrementer() { compteur++; }
18. Design Patterns essentiels
Les design patterns sont des solutions éprouvées à des problèmes récurrents de conception. Voici
les 4 patterns les plus utilisés en Java.
① Singleton — une seule instance
public class Config { private static Config instance; private Config() {} //
constructeur privé public static synchronized Config getInstance() { if (instance ==
null) instance = new Config(); return instance; } public String getEnv() { return
"production"; } } // Usage Config cfg = [Link]();
② Builder — construction étape par étape
public class Requete { private final String url, methode; private final int timeout;
private Requete(Builder b) { [Link] = [Link]; [Link] = [Link]; [Link]
= [Link]; } public static class Builder { private String url, methode = "GET";
private int timeout = 30; public Builder url(String url) { [Link] = url; return
this; } public Builder methode(String m) { [Link] = m; return this; } public
Builder timeout(int t) { [Link] = t; return this; } public Requete build() {
return new Requete(this); } } } Requete r = new
[Link]().url("[Link]
③ Observer — notification d'événements
import [Link].*; interface Observateur { void notifier(String event); } class
BoutonCliquable { private List obs = new ArrayList<>(); public void
ajouter(Observateur o) { [Link](o); } public void cliquer() { [Link](o ->
[Link]("CLIC")); } } BoutonCliquable btn = new BoutonCliquable(); [Link](e ->
[Link]("Logger: " + e)); [Link](e -> [Link]("Analytics: "
+ e)); [Link](); // Logger: CLIC / Analytics: CLIC
④ Factory — déléguer la création d'objets
interface Forme { double aire(); } record Cercle(double r) implements Forme { public
double aire(){ return [Link]*r*r; } } record Carre(double c) implements Forme {
public double aire(){ return c*c; } } class FormeFactory { public static Forme
creer(String type, double val) { return switch (type) { case "cercle" -> new
Cercle(val); case "carre" -> new Carre(val); default -> throw new
IllegalArgumentException("Type inconnu"); }; } } Forme f =
[Link]("cercle", 5); [Link]("Aire : %.2f%n", [Link]()); //
78.54
19. Entrées / Sorties (I/O & NIO)
Java propose deux APIs : l'ancienne [Link] et la moderne [Link] (recommandée depuis Java
7) via la classe utilitaire Files et Path.
Lire et écrire un fichier — [Link] (recommandé)
import [Link].*; import [Link]; // Écrire dans un fichier Path
fichier = [Link]("[Link]"); String contenu = "Ligne 1\nLigne 2\nLigne 3";
[Link](fichier, contenu); // Lire tout le fichier d'un coup String texte =
[Link](fichier); [Link](texte); // Lire ligne par ligne (Stream)
[Link](fichier) .filter(l -> [Link]("1")) .forEach([Link]::println); //
Ligne 1 // Lister les fichiers d'un dossier [Link]([Link](".")) .filter(p ->
[Link]().endsWith(".txt")) .forEach([Link]::println); // Copier / Déplacer
[Link]([Link]("[Link]"), [Link]("[Link]"),
StandardCopyOption.REPLACE_EXISTING); [Link]([Link]("[Link]"),
[Link]("[Link]"), StandardCopyOption.ATOMIC_MOVE); // Vérifications
[Link](fichier); // true / false [Link](fichier); // false
[Link](fichier); // taille en octets
Lecture interactive — Scanner
import [Link]; Scanner sc = new Scanner([Link]);
[Link]("Entrez votre nom : "); String nom = [Link]();
[Link]("Entrez votre âge : "); int age = [Link]();
[Link]("Bonjour " + nom + ", vous avez " + age + " ans."); [Link]();
Sérialisation — Persister un objet
import [Link].*; // La classe doit implémenter Serializable public class Personne
implements Serializable { private static final long serialVersionUID = 1L; String nom;
int age; Personne(String nom, int age){ [Link]=nom; [Link]=age; } } // Écrire try
(ObjectOutputStream out = new ObjectOutputStream( new
FileOutputStream("[Link]"))) { [Link](new Personne("Alice", 30)); } //
Lire try (ObjectInputStream in = new ObjectInputStream( new
FileInputStream("[Link]"))) { Personne p = (Personne) [Link]();
[Link]([Link] + " / " + [Link]); // Alice / 30 }
20. Bonnes pratiques & Java moderne
Java évolue rapidement. Voici les fonctionnalités modernes (Java 14–21) et les meilleures pratiques
à adopter dès maintenant.
Records — classes de données immuables (Java 16+)
// Équivaut à une classe avec constructeur, getters, equals, hashCode, toString record
Point(double x, double y) { // Méthode personnalisée possible public double
distance(Point autre) { return [Link]([Link](x-autre.x,2) +
[Link](y-autre.y,2)); } } Point p1 = new Point(0, 0), p2 = new Point(3, 4);
[Link](p1.x()); // 0.0 [Link]([Link](p2)); // 5.0
Sealed Classes — hiérarchie contrôlée (Java 17+)
sealed interface Forme permits Cercle, Rectangle, Triangle {} record Cercle(double r)
implements Forme {} record Rectangle(double l, double h) implements Forme {} record
Triangle(double b, double h) implements Forme {}
Pattern Matching switch (Java 21)
double aire = switch (forme) { case Cercle c -> [Link] * c.r() * c.r(); case
Rectangle r -> r.l() * r.h(); case Triangle t -> 0.5 * t.b() * t.h(); };
Text Blocks — chaînes multilignes (Java 15+)
String json = """ { "nom": "Alice", "age": 30 } """; [Link](json);
Meilleures pratiques — synthèse
Pratique Pourquoi ?
Préférer var (Java 10+) Inférence de type, code plus court
Utiliser final autant que possible Immutabilité, moins de bugs
Favoriser les interfaces sur les classes concrètes Découplage, testabilité
Éviter null — utiliser Optional Prévient NullPointerException
Logger avec SLF4J, pas [Link] Logs configurables en production
Écrire des tests unitaires (JUnit 5) Confiance dans le code, refactoring sûr
Respecter les principes SOLID Architecture maintenable à long terme
■ Félicitations ! Vous maîtrisez désormais les concepts avancés de Java. Prochaine étape : Spring
Boot, Quarkus, architecture microservices.