Évolution de Java : de Java 11 aux versions LTS modernes
Prérequis
Connaître les bases de la programmation orientée objet en Java. Avoir une expérience minimale avec Java 8 ou une version antérieure.
Objectifs
Comprendre l'évolution du langage Java à travers ses versions LTS (Long-Term Support), identifier les nouvelles fonctionnalités majeures, et savoir choisir la version adaptée à un projet professionnel.
1. Contexte et cadence des versions
Un peu d'histoire
Pendant des décennies, Java a été l'un des langages de programmation les plus utilisés dans les logiciels d'entreprise. À partir de Java 9 (2017), la cadence de sortie est passée à tous les six mois, avec une version mineure tous les deux mois. Cela permet des améliorations plus fréquentes et moins risquées.
Les versions LTS actuellement supportées sont :
| Version | Date de sortie | Type | Support Oracle (Premier) |
|---|---|---|---|
| Java 8 | Mars 2014 | LTS | Indéfini (usage personnel/dev) |
| Java 11 | Septembre 2018 | LTS | Jusqu'en 2026 |
| Java 17 | Septembre 2021 | LTS | Jusqu'en 2026 |
| Java 21 | Septembre 2023 | LTS | Jusqu'en 2028 |
| Java 25 | Septembre 2025 | LTS | Jusqu'en 2030 |
2. Java 11 — Première LTS moderne
2.1 Convergence Oracle JDK / OpenJDK
OpenJDK, l'équivalent open source du JDK Oracle, existe depuis Java 7. Avec Java 11, les fonctionnalités commerciales autrefois réservées à Oracle JDK (Flight Recorder, Mission Control, Z Garbage Collector) sont devenues gratuites et open source dans OpenJDK.
2.2 Nouvelles fonctionnalités du langage
Client HTTP natif (HttpClient)
Jusqu'à Java 10, les développeurs devaient recourir à des bibliothèques externes comme Apache HttpComponents pour gérer HTTP/2 ou les WebSockets. Java 11 intègre nativement java.net.http.HttpClient, devenu une fonctionnalité finale.
Exemple — Requête GET avec HttpClient
import java.net.http.*;
import java.net.URI;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.exemple.com/données"))
.GET()
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
{"id": 1, "nom": "Java", "version": 11}
- Créer une instance
HttpClientet la configurer. - Construire une instance
HttpRequestavec l'URI et la méthode. - Envoyer la requête et récupérer un
HttpResponse. - Traiter la réponse.
Lancer un programme sans compilation préalable
Java 11 permet d'exécuter directement un fichier source .java sans passer par javac, idéal pour tester rapidement de petits extraits de code.
java MonProgramme.java
java.base. L'appel de méthodes définies dans d'autres fichiers source n'est pas supporté.Nouvelles méthodes — API String
Java 11 enrichit l'API String avec quatre nouvelles méthodes pratiques :
| Méthode | Description | Exemple |
|---|---|---|
repeat(n) |
Répète la chaîne n fois | "ab".repeat(3) → "ababab" |
isBlank() |
Vrai si la chaîne est vide ou ne contient que des espaces | " ".isBlank() → true |
strip() |
Supprime les espaces de début et de fin (Unicode-aware) | " hi ".strip() → "hi" |
lines() |
Retourne un Stream des lignes du texte |
"a\nb".lines().count() → 2 |
strip() à trim() : la première est conforme au standard Unicode et traite correctement les espaces non-ASCII (ex : espace insécable \u00A0).API Files simplifiée
// Lire un fichier entier en une ligne
String contenu = Files.readString(Path.of("données.txt"));
// Écrire dans un fichier
Files.writeString(Path.of("sortie.txt"), "Bonjour Java 11 !");
Optional et Predicate
// isEmpty() — nouvel opposé de isPresent()
Optional<String> opt = Optional.empty();
if (opt.isEmpty()) System.out.println("Aucune valeur");
// Predicate.not() — filtrage élégant
List<String> nonVides = liste.stream()
.filter(Predicate.not(String::isBlank))
.toList();
Mot-clé var dans les expressions lambda
// var permet d'ajouter des annotations sur les paramètres lambda
liste.stream()
.map((@NotNull var s) -> s.toUpperCase())
.forEach(System.out::println);
2.3 Performance et sécurité
Java 11 améliore le ramasse-miettes G1 (par défaut depuis Java 9), réduisant les temps de pause de près de 60% sur les processeurs x64. Deux nouveaux GC expérimentaux font leur apparition : ZGC et Epsilon.
Du côté sécurité, Java 11 intègre nativement le support de TLS 1.3, offrant des échanges HTTPS plus rapides et plus sûrs.
3. Java 17 — LTS recommandée pour les projets modernes
Complément
Java 17 (septembre 2021) est la deuxième LTS depuis le changement de cadence. Elle apporte des fonctionnalités majeures de productivité et renforce l'encapsulation du module system.
3.1 Records — classes de données immuables
equals(), hashCode() et toString().Exemple — Déclaration d'un Record
// Déclaration compacte
record Point(int x, int y) {}
// Utilisation
Point p = new Point(3, 5);
System.out.println(p.x()); // 3
System.out.println(p); // Point[x=3, y=5]
3 Point[x=3, y=5]
3.2 Classes scellées (sealed)
public abstract sealed class Forme
permits Cercle, Rectangle, Triangle {}
public final class Cercle extends Forme {
double rayon;
}
public final class Rectangle extends Forme {
double largeur, hauteur;
}
switch, rendant le code plus sûr et plus expressif — particulièrement utile combiné au pattern matching (voir section suivante).3.3 Pattern Matching pour instanceof
Avant Java 16/17, une vérification de type nécessitait un cast explicite :
// ❌ Ancien style (Java < 16)
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.toUpperCase());
}
// ✅ Nouveau style (Java 17)
if (obj instanceof String s) {
System.out.println(s.toUpperCase());
}
s) est disponible uniquement dans la branche if où le test est vrai. Le compilateur garantit la sûreté de type.3.4 Encapsulation forte des modules JDK
Java 17 rend l'encapsulation des modules stricte par défaut : l'accès réflexif aux API internes de la JDK (sun.*, com.sun.*) est désormais bloqué sans configuration explicite.
4. Java 21 — LTS actuelle, fonctionnalités de rupture
Complément
Java 21 (septembre 2023) est la LTS la plus récente et largement adoptée. Elle finalise plusieurs fonctionnalités expérimentales et introduit un changement de paradigme majeur avec les threads virtuels.
4.1 Threads virtuels (Project Loom)
Exemple — Créer des threads virtuels
// Thread virtuel simple
Thread t = Thread.ofVirtual().start(() -> {
System.out.println("Exécuté sur un thread virtuel !");
});
// Avec un ExecutorService dédié
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1_000_000; i++) {
int id = i;
executor.submit(() -> traiter(id));
}
} // fermeture automatique
4.2 Pattern Matching pour switch
// Combinaison parfaite avec les classes scellées
String décrire(Forme f) {
return switch (f) {
case Cercle c -> "Cercle de rayon " + c.rayon();
case Rectangle r -> "Rectangle " + r.largeur() + "x" + r.hauteur();
case Triangle t -> "Triangle";
};
// Le compilateur vérifie l'exhaustivité grâce aux classes scellées !
}
case Cercle c when c.rayon() > 10 -> "Grand cercle";
4.3 Record Patterns — déconstruction dans les patterns
record Point(int x, int y) {}
// Déconstruction directe dans le pattern
if (obj instanceof Point(int x, int y)) {
System.out.println("x=" + x + ", y=" + y);
}
// Dans un switch
String décrire(Object obj) {
return switch (obj) {
case Point(int x, int y) -> "Point(" + x + ", " + y + ")";
default -> "Inconnu";
};
}
4.4 Séquenced Collections
SequencedCollection, une nouvelle interface pour les collections ayant un ordre défini (premier/dernier élément accessibles directement).
List<String> liste = new ArrayList<>(List.of("a", "b", "c"));
// Nouvelles méthodes unifiées
liste.getFirst(); // "a"
liste.getLast(); // "c"
liste.addFirst("z");
liste.reversed(); // vue inversée sans copie
5. Choisir sa version Java
| Situation | Version recommandée | Raison |
|---|---|---|
| Nouveau projet professionnel | Java 21 | LTS actuelle, threads virtuels, pattern matching complet |
| Projet existant sous Java 11 | Java 17 puis 21 | Migration progressive par paliers LTS |
| Enseignement / apprentissage | Java 21 | Syntaxe moderne, records, pattern matching |
| Contrainte de stabilité maximale | Java 17 | Très bien supporté par tous les frameworks |
6. Exercice de synthèse
Modélisation avec Records et Pattern Matching
On souhaite modéliser différents types de messages dans une application de messagerie. Un message peut être : un TexteMessage (contenu texte), un ImageMessage (URL et légende), ou un FichierMessage (nom de fichier et taille en octets).
Travail demandé
- Déclarer une classe scellée
Messageet ses trois sous-types en tant que records. - Écrire une méthode
résumé(Message m)utilisant le pattern matching sur switch pour retourner une description textuelle du message. - Tester avec une liste de messages de types variés.
- Utiliser Java 21 (records + sealed classes + pattern matching)
- La méthode
résumé()doit être exhaustive (pas dedefault) - La taille d'un
FichierMessages'affiche en Ko si ≥ 1024, en octets sinon
Texte : "Bonjour tout le monde !" Image : photo.jpg — "Coucher de soleil" Fichier : rapport.pdf (12 Ko)
Étape 1 — Déclaration des types
// ── Types scellés + records ──────────────────────────────
sealed interface Message
permits TexteMessage, ImageMessage, FichierMessage {}
record TexteMessage(String contenu) implements Message {}
record ImageMessage(String url, String légende) implements Message {}
record FichierMessage(String nom, long tailleOctets) implements Message {}
// ── Méthode résumé avec pattern matching ─────────────────
static String résumé(Message m) {
return switch (m) {
case TexteMessage(var c) -> "Texte : \"" + c + "\"";
case ImageMessage(var u, var l) -> "Image : " + u + " — \"" + l + "\"";
case FichierMessage(var n, var t)
when t >= 1024 -> "Fichier : " + n + " (" + t/1024 + " Ko)";
case FichierMessage(var n, var t) -> "Fichier : " + n + " (" + t + " octets)";
};
}
// ── Test ─────────────────────────────────────────────────
public static void main(String[] args) {
List<Message> messages = List.of(
new TexteMessage("Bonjour tout le monde !"),
new ImageMessage("photo.jpg", "Coucher de soleil"),
new FichierMessage("rapport.pdf", 12_288)
);
messages.stream()
.map(Main::résumé)
.forEach(System.out::println);
}
// ── Sans records ni pattern matching (Java 8) ────────────
// Nécessite des classes séparées avec constructeurs,
// getters, equals(), hashCode(), toString() pour chaque type
// + une série de if/instanceof imbriqués
static String résumé(Object m) {
if (m instanceof TexteMessage) {
return "Texte : \"" + ((TexteMessage) m).getContenu() + "\"";
} else if (m instanceof ImageMessage) {
ImageMessage img = (ImageMessage) m;
return "Image : " + img.getUrl() + " — \"" + img.getLégende() + "\"";
} else if (m instanceof FichierMessage) {
FichierMessage f = (FichierMessage) m;
long t = f.getTailleOctets();
return "Fichier : " + f.getNom() + (t >= 1024 ? " (" + t/1024 + " Ko)" : " (" + t + " octets)");
}
throw new IllegalArgumentException("Type inconnu");
}
Texte : "Bonjour tout le monde !" Image : photo.jpg — "Coucher de soleil" Fichier : rapport.pdf (12 Ko)
- Sealed interface + records : éliminent le code répétitif des classes de données.
- Pattern matching exhaustif : le compilateur garantit que tous les cas sont traités.
- Record patterns : la déconstruction
FichierMessage(var n, var t)extrait directement les champs sans appeler de getter. - Guards (
when) : permettent d'affiner les cas sansifimbriqués.
L'essentiel en bref
Java évolue rapidement grâce à sa cadence semestrielle, avec des versions LTS tous les deux ans. Java 11 a modernisé les bases (HttpClient natif, nouvelles API String/Files). Java 17 a introduit records, classes scellées et pattern matching pour instanceof. Java 21 finalise le pattern matching complet (switch, record patterns), introduit les threads virtuels (Project Loom) et les SequencedCollections. Pour tout nouveau développement, Java 21 est aujourd'hui la référence.
Discussion (0)
Soyez le premier à laisser un commentaire !
Laisser un commentaire
Votre commentaire sera visible après modération.