Intro
Le 16 septembre 2025, la nouvelle version à support long terme (LTS) de Java sera publiée : Java 25 avec 18 fonctionnalités et une maintenance prévue jusqu’en 2030. Dans cet article, je mets en avant ce que je considère comme les fonctionnalités clés de cette version.
JEP-470 PEM Encodings of Cryptographic Objects (Preview)
Le format PEM (Privacy Enhanced Mail) est un encodage texte largement utilisé pour représenter des clés cryptographiques, des certificats et d’autres données liées à la sécurité. Un fichier PEM est facile à reconnaître : il contient les données encodées en base64, entourées de lignes d’en-tête et de pied comme :
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi/kRGOL7wCPTN4KJ2ppeSt5UYB6u
cPjjuKDtFTXbguOIFDdZ65O/8HTUqS/sVzRF+dg7H3/tkQ/36KdtuADbwQ==
-----END PUBLIC KEY-----Avec cette fonctionnalité en preview, Java supporte désormais l’encodage et le décodage des données PEM via une API standard. Il est désormais beaucoup plus simple de travailler avec ce format dans vos applications, comme pour la gestion de certificat TLS/SSL ou par exemple l’authentification à des outils comme AWS, Kubernetes etc… Par exemple, voici comment encoder une clé privée avec un mot de passe au format PEM :
public class SimplePemExample {
public static void main(String[] args) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair pair = keyGen.generateKeyPair();
PublicKey publicKey = pair.getPublic();
// Encoder la clé publique au format PEM (en tant que String)
String pem = PEMEncoder.of().encodeToString(publicKey);
// Décoder le PEM pour obtenir un objet PublicKey
PublicKey decoded = PEMDecoder.of().decode(pem, PublicKey.class);
}
}N’oubliez pas que pour utiliser les fonctionnalités en preview dans Java, vous devrez activé le mode preview au lancement de l’application:
java --enable-preview MyApplicationJEP-502 Stable Values (Preview)
Cette fonctionnalité en preview introduit une nouvelle API qui permet de créer une valeur immutable sans nécessiter une initialisation immédiate. Avec les valeurs stables, vous pouvez différer l’init d’une variable jusqu’à ce qu’elle soit réellement nécessaire, tout en garantissant qu’elle n’est définie qu’une seule fois. Cette approche est particulièrement utile pour le lazy loading de ressources ou d’objets coûteux. Une fois la valeur définie, elle devient finale — comme un champ final classique, mais avec une flexibilité accrue et un meilleur support pour les environnements multithreads.
public class Main {
public static void main(String[] args) {
StableValue<DatabaseConnection> dbConn = StableValue.of();
DatabaseConnection conn1 = dbConn.orElseSet(() -> new DatabaseConnection("jdbc:demo"));
conn1.query("SELECT * FROM users");
}
}JEP-503 Remove the 32-bits x86 Port
Déprécié dans Java 24, le port 32-bits x86 est désormais supprimé dans cette version. La motivation derrière cette suppression était le coût croissant de maintenance pour supporter de nouvelles fonctionnalités sur cette architecture, rarement utilisée. Par conséquent, si vous essayez d’exécuter du code Java 25 sur un système 32-bits x86, cela ne fonctionnera pas. Cependant, le support pour d’autres systèmes 32-bits, comme ARM, reste disponible. Il existe encore une solution pour exécuter Java sur des architectures non supportées : vous pouvez utiliser le port « Zero », une implémentation générique de la JVM qui ne repose pas sur des optimisations spécifiques au matériel. Bien qu’il ne soit pas aussi rapide que les ports spécifiques à une architecture, il permet à Java de fonctionner sur une large gamme de plateformes. Vous trouverez la documentation officielle du port Zero ici :
https://openjdk.org/projects/zero/
JEP-509 JFR Cpu-Time Profiling (Experimental)
Cette fonctionnalité, permet d’améliorer l’observabilité de la consommation CPU dans Java Flight Recorder (JFR).
Cette fonctionnalité est actuellement expérimentale et disponible uniquement sur les systèmes Linux.
Avant ce JEP, toutes les 20 millisecondes de temps réel, JFR effectuait un « tick » et échantillonnait la pile d’exécution du thread en train d’exécuter du bytecode Java à ce moment précis. Imaginez qu’un thread ait été très actif pendant 19 ms, mais qu’au moment exact du tick JFR, il soit en attente ou bloqué : il ne sera pas comptabilisé dans l’échantillonnage, et son activité CPU récente sera manquée.
De plus, le temps passé à exécuter du code natif (comme du JNI ou des appels système) n’était pas correctement observé, car JFR n’échantillonnait que les threads exécutant du bytecode Java, rendant l’exécution native largement invisible au profiler.
JEP-509 introduit un nouveau type d’événement, jdk.CPUTimeSample, qui utilise le timer CPU de Linux pour échantillonner les threads en fonction du temps CPU réel qu’ils ont consommé.
Avec cette approche, chaque thread est échantillonné après avoir effectivement consommé une certaine quantité de temps CPU, indépendamment du fait qu’il soit actif ou en attente.
En conséquence, l’utilisation CPU est mesurée de manière beaucoup plus précise, et le temps passé dans le code natif est désormais inclus dans le profilage.
Pour activer cet événement lors du démarrage de votre application, utilisez la commande suivante :
java -XX:StartFlightRecording=jdk.CPUTimeSample#enabled=true,filename=profile.JEP-511: Module Import Declarations
Avec Java 25, une nouvelle façon d’importer apparaît : la déclaration d’import de module. Cette fonctionnalité permet d’importer toutes les classes et interfaces publiques d’un module en une seule ligne, rendant votre code moins verbeux et plus facile à lire — surtout lorsque vous devez utiliser de nombreuses classes du même module. Par exemple, au lieu d’écrire plusieurs instructions d’import comme :
import java.util.*;
import java.io.*;vous pouvez désormais simplement écrire :
import module java.base;Cela met à disposition toutes les classes utiles de java.base, comme List, Map ou Path, sans avoir à vous soucier des packages individuels. Cela est particulièrement pratique pour les débutants ou lorsque vous souhaitez prototyper rapidement et utiliser une large gamme de classes standard.
Attention cependant : si deux modules exportent des classes avec le même nom (comme Date dans java.util et java.sql), vous obtiendrez une erreur de compilation. Dans ce cas, il suffit d’ajouter un import spécifique pour résoudre l’ambiguïté :
import module java.base;
import java.sql.Date;JEP-512 : Compact Source Files and Instance Main Methods
Java 25 introduit les fichiers sources compacts, rendant plus facile que jamais l’écriture de scripts simples et intuitifs — surtout pour les développeurs débutants. Avec cette fonctionnalité, vous pouvez désormais écrire des programmes Java sans le code standard : il n’est plus nécessaire de déclarer explicitement une classe ou une méthode main statique. Par exemple, vous pouvez simplement écrire :
void main() {
IO.println("Hello, World!");
}La nouvelle classe IO, qui fournit des méthodes simples pour les entrées/sorties console. Au lieu de System.out.println("Hello, World!");, vous pouvez désormais utiliser IO.println("Hello, World!");, ce qui rend le code plus lisible et accessible.
Ces changements offrent une courbe d’apprentissage plus douce pour les concepts de programmation et permettent d’écrire et d’exécuter de petits programmes Java de manière plus concise, tout en restant pleinement compatibles avec la chaîne d’outils Java standard.
JEP-513 Flexible Constructor Bodies
Le Flexible Constructor Bodies devient une fonctionnalité finalisée. Cette amélioration permet d’écrire des validations ou d’effectuer d’autres logiques d’initialisation avant d’appeler super(…) ou this(…) dans un constructeur, au lieu d’être obligé de placer cet appel en tout premier.
Ainsi, vous pouvez désormais échouer rapidement sur des arguments invalides, préparer un état partagé ou gérer une configuration complexe avant de déléguer à la superclasse ou à un autre constructeur. Cela élimine les duplications de code inutiles et améliore à la fois la sécurité et la lisibilité, tout en exigeant que chaque constructeur contienne exactement un appel à un constructeur de superclasse.
Voici la nouvelle façon possible de créer un constructeur :
class Animal {
final String name;
Animal(String name) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("Name must not be empty");
}
this.name = name;
}
}
class Bird extends Animal {
final double flightSpeed;
Bird(String name, double wingSpan) {
if (flightSpeed < 330.0) {
throw new IllegalArgumentException(" must be at least 330 km/h");
}
super(name); // Peut maintenant être après la validation
this.wingSpan = wingSpan;
}
}Conclusion
Dans cet article, nous avons exploré plusieurs JDK Enhancement Proposals (JEPs) introduites dans Java 25. Au-delà des fonctionnalités discutées, cette nouvelle version apporte d’autres améliorations, comme le support des primitives dans les instructions switch et instanceof avec JEP-502, un meilleur traçage avec la JEP-520, et le mode générationnel expérimental pour le garbage collector Shenandoah, parmi beaucoup d’autres. Je vous encourage à consulter la liste complète des JEPs inclus dans Java 25. À mon avis, cette version n’est pas aussi révolutionnaire que la précédente version LTS, Java 21, qui avait introduit les virtual threads. Cependant, je trouve plusieurs nouvelles fonctionnalités assez intéressantes, comme l’API pour manipuler les fichiers PEM, des outils de monitoring améliorés, les fichiers sources compacts pour le scripting, et les Valeurs Stables, ainsi que le flexible constructor bodies. Ces améliorations rendent le code plus flexible et plus facile à écrire.


_Z17FUgS.webp)