Java 14 : Vue d’ensemble

Ce premier article propose une vue d’ensemble des fonctionnalités de Java 14.
Cet article fait partie d’une série détaillant les principales fonctionnalités proposées dans Java 14 :

 

En application du modèle de releases tous les 6 mois, mis en oeuvre depuis 2 ans maintenant, Java 14 est diffusé en version General Availability le 17 mars 2020, 6 mois après la release de Java 13.

C’est la 5eme version de Java publiée sous ce modèle cadencé de releases.

La version 14 Java n’est pas une version LTS, donc elle sera obsolète 6 mois après sa sortie, lorsque Java 15 sera diffusé en septembre prochain.

Les principaux fournisseurs de distributions non LTS gratuites d’OpenJDK proposent en téléchargement un JDK 14 pour différentes plateformes :

Java 14 regroupe 16 JEPs, ce qui est un nombre supérieur par rapport aux deux versions précédentes réunies :

Ces JEPs ne sont pas que des ajouts, certaines concernent des dépréciations et même des suppressions de fonctionnalités.

Les fonctionnalités pour les développeurs

Java 14 propose plusieurs améliorations syntaxiques dans le langage, dont certaines sont en preview : switch expression, records, pattern matching pour l’instruction instanceof,  text blocks, …

 

JEP 305: Pattern Matching for instanceof (Preview)

Cette JEP permet d’utiliser le pattern matching sur l’opérateur instanceof.

Historiquement l’opérateur instanceof permet de vérifier le type d’un objet avant de pouvoir effectuer un cast vers le type assigné à une nouvelle variable.

if (obj instanceof Groupe) {
  Groupe groupe = (Groupe) obj;
  var membres= groupe.getMembres();
}

L’opérateur instanceof peut utiliser la pattern matching afin de réduire la quantité de code pour obtenir le même résultat

if (obj instanceof Groupe groupe) {
  var membres= groupe.getMembres();
}

Cette fonctionnalité est proposée en preview. Elle sera détaillée dans un autre article de cette série.

 

JEP 359: Records (Preview)

Cette JEP introduit un nouveau type dans le langage Java pour faciliter la définition d’une classe qui encapsule des données de manière immuable. La syntaxe concise d’un record tranche radicalement avec celle historique requise pour la création d’une telle classe qui inclut un constructeur, des getters, et une redéfinition des méthode equals(), hashCode() et toString().

package com.oxiane.java14.records;

import java.time.Duration;

public record Formation(String titre, Duration duree) {
}

Cette fonctionnalité est proposée en preview. Elle sera détaillée dans un article dédié de cette série.

 

JEP 361: Switch Expressions (Standard)

Initialement proposé en mode preview en Java 12 puis 13, l’instruction switch voit sa syntaxe évoluer et il est maintenant possible de l’utiliser comme une expression puisqu’elle peut retourner une valeur.

Cette fonctionnalité sort du mode preview pour être maintenant standard en Java 14. Elle sera détaillée dans un article dédié de cette série.

 

JEP 368: Text Blocks (Second Preview)

Les Text Blocks permettent d’exprimer de manière plus simple une chaîne de caractères littérale multi-lignes. Proposée initialement en preview Java 13, une seconde preview est proposée en Java 14 incluant le support de deux nouvelles séquences d’échappement.

Cette fonctionnalité sera détaillée dans un article dédié de cette série.

 

Java propose une fonctionnalité qui va s’avérer pratique lorsqu’une exception de type NullPointerException est levée.

JEP 358: Helpful NullPointerExceptions

L’exception de type NullPointerException (NPE) est celle que l’on rencontre le plus fréquemment lors de l’exécution d’une application Java.

Elle est levée par la JVM lorsque l’on tente d’utiliser une référence dont la valeur est null.

public class TestNPE {

  public static void main(String... args) {
    String message=null;
    System.out.println(message.toUpperCase());
  }
}

Malheureusement, les informations fournies par une NPE ne sont pas utiles hormis la ligne concernée dans la stacktrace.

C:\java>javac TestNPE.java

C:\java>java TestNPE
Exception in thread "main" java.lang.NullPointerException
        at TestNPE.main(TestNPE.java:5)

Sauf pour des cas simples, généralement pour déterminer quel est l’élément dont la référence est null, il faut déboguer ou ajouter des traces.

Le but de la JEP 358 « Helpful NullPointerExceptions » est de permettre à la JVM de réaliser une analyse du byte-code pour fournir un message d’erreur plus utile sur la conséquence et la cause.

Cette fonctionnalité n’est pas activée par défaut dans la version 14 de la JVM. Elle pourra l’être dans une future version. En attendant, il faut activer l’option -XX:+ShowCodeDetailsInExceptionMessages

Inutile de recompiler le code car c’est une fonctionnalité intégrée dans la JVM.

C:\java>java -XX:+ShowCodeDetailsInExceptionMessages TestNPE
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "" is null
        at TestNPE.main(TestNPE.java:5)

Le message est d’autant plus précis si le byte-code contient les informations de débogage : pour cela il faut utiliser l’option -g du compilateur

C:\java>javac -g TestNPE.java

 

C:\java>java -XX:+ShowCodeDetailsInExceptionMessages TestNPE
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "message" is null
        at TestNPE.main(TestNPE.java:5)

Note : le message fourni est différent en fonction du contexte de levée du NPE : accès à un attribut, invocation d’une méthode, accès à un tableau, accès à la taille d’un tableau, …

Il est aussi important de noter que cette fonctionnalité n’est pas toujours mise en œuvre notamment, pour les NPE levées dans le code, pour les références de méthodes ou pour les méthodes cachées (hidden methods) générées par le compilateur.

 

Java 14 propose aussi de nouvelles API.

JEP 349: JFR Event Streaming

Le Java Flight Recorder est intégré dans OpenJDK depuis Java 11. Jusqu’à présent pour exploiter les informations contenues dans les événements capturés par le Flight Recorder dans la JVM, il fallait passer par un fichier binaire et utiliser un outil comme JMC.

Cette JEP propose une API qui permet d’obtenir un flux, exploitable en temps réel, des événements auxquels on est abonné.

    try (var rs = new RecordingStream()) {
       rs.enable("jdk.CPULoad").withPeriod(Duration.ofMillis(500));
       rs.onEvent("jdk.CPULoad", event -> {
         System.out.println(event.getFloat("machineTotal"));
       });
       rs.start();
    }

Cela devrait permettre de développer plus facilement des fonctionnalités de monitoring.

 

JEP 370: Foreign-Memory Access API (Incubator)

Cette JEP définit une API de bas niveau qui permet d’accéder de manière sûre et performante à des données en mémoire hors du tas (off heap memory).

Cette fonctionnalité est proposée en incubator dans le module jdk.incubator.foreign.

import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;

import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemorySegment;

public class TestForeign {

  public static void main(String[] args) {
    final VarHandle intVarHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());

    try (MemorySegment segment = MemorySegment.allocateNative(1024)) {
      MemoryAddress baseAdress = segment.baseAddress();
      intVarHandle.set(baseAdress, 256);
      intVarHandle.set(baseAdress.addOffset(4), 512);

      System.out.println(baseAdress);
      System.out.println(intVarHandle.get(baseAdress));
      System.out.println(intVarHandle.get(baseAdress.addOffset(4)));
    }
  }
}

A l’exécution de ce code, un avertissement est affiché car le code utilise un module en incubator.

WARNING: Using incubator modules: jdk.incubator.foreign
MemoryAddress{ region: MemorySegment{ id=0x6dc2af28 limit: 1024 } offset=0x0 }
256
512

Il ne faut pas oublier de déclarer le module jdk.incubator.foreign en tant que dépendance dans le descripteur de module.

  requires jdk.incubator.foreign;

Cette API pourra être une alternative aux classes java.nio.ByteBuffer et sun.misc.Unsafe.

 

Les fonctionnalités en preview

Plusieurs fonctionnalités dans le langage en sont mode preview (défini dans la JEP 12). Cela implique plusieurs choses :

  • Elles peuvent être utilisées mais sont susceptibles d’être modifiées en fonction du feedback obtenu avant de devenir standard ou même éventuellement d’être abandonnées
  • Leur utilisation n’est pas activée par défaut : elle requiert des paramètres particuliers au compilateur et à la JVM

Pour utiliser les fonctionnalités du langage en mode preview, il faut utiliser l’option -​-enable-preview des outils javac, java et jshell.

Pour le compilateur javac, il faut en plus utiliser l’option -​-source ou -​-release en indiquant la version de Java utilisée.

javac --release 14 --enable-preview MaClasse.java
java --enable-preview MaClasse
java --enable-preview --source 14 AutreClasse.java
jshell --enable-preview

L’utilisation de fonctionnalités en preview sans ces options provoque une erreur.

C:\java>javac MonRecord.java
MonRecord.java:1: error: records are a preview feature and are disabled by default.
public record MonRecord(String titre) {
       ^
  (use --enable-preview to enable records)
1 error

C:\java>javac --enable-preview MonRecord.java
error: --enable-preview must be used with either -source or --release

C:\java>javac --enable-preview --release 14 MonRecord.java
Note: MonRecord.java uses preview language features.
Note: Recompile with -Xlint:preview for details.

C:\java>java MonRecord
Erreur : LinkageError lors du chargement de la classe principale MonRecord
        java.lang.UnsupportedClassVersionError: Preview features are not enabled for MonRecord (class file version 58.65535). Try running with '--enable-preview'

C:\java>java --enable-preview MonRecord

C:\java> 

 

Le support par les IDE

La version 2020-03 d’Eclipse ne propose pas nativement un support de Java 14. Il faut ajouter un plugin qui n’est proposé que pour cette dernière version d’Eclipse. Ce plugin est disponible sur le marketplace d’Eclipse.

java14-1-1

Le plugin apporte entre autre le compilateur pour Java 14, le support des options dans les propriétés des projets notamment l’utilisation des fonctionnalités en mode preview, le support de la syntaxe dans l’éditeur de code du JDT.

java14-1-2

 

Le support de Java 14 par Intellij IDEA est proposé dans sa version 2020.1 qui a été publiée le 8 avril 2020.

java14-1-3

Par activer l’utilisation des fonctionnalités en preview, il faut sélectionner « 14 (Preview) – Records, patterns, text blocks) dans le niveau du langage pour le projet (Project language level).

java14-1-4

 

La version 11.3 de NetBeans, qui n’est pas LTS, propose un support des fonctionnalités de Java 14 sous réserve que NetBeans soit obligatoirement exécuté avec un JDK 14.

https://netbeans.apache.org/download/nb113/index.html

 

Il y a aussi l’outil jshell, qui est un REPL, fourni avec le JDK pour tester du code dont les fonctionnalités de Java 14.

 

Les fonctionnalités concernant la JVM

Plusieurs fonctionnalités concernent la JVM, essentiellement les ramasses-miettes. Ces fonctionnalités concernent des ajouts, de la dépréciation et même des retraits comme c’est le cas pour le ramasse-miettes CMS.

 

JEP 345: NUMA-Aware Memory Allocation for G1

Le but est d’améliorer les performances de G1 sur de grosses machines en proposant un support de l’allocation de mémoire compatible NUMA. L’implémentation n’est faite que pour Linux

Pour activer ce support pour G1, il faut utiliser l’option -XX:+UseNUMA

$java -XX:+UseNUMA -version
openjdk version "14" 2020-03-17
OpenJDK Runtime Environment AdoptOpenJDK (build 14+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14+36, mixed mode, sharing)

 

JEP 362: Deprecate the Solaris and SPARC Ports

Les portages Solaris/SPARC, Solaris/x64 et Linux/SPARC sont dépréciés avec l’intention de les retirer dans une future version.

 

JEP 363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector

Java 9 avait déprécié le ramasse-miettes CMS via la JEP 291, il est retiré en Java 14.

Son utilisation est ignorée avec un message d’avertissement.

C:\java>java -XX:+UseConcMarkSweepGC -version
OpenJDK 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC; support was removed in 14.0
openjdk version "14" 2020-03-17
OpenJDK Runtime Environment AdoptOpenJDK (build 14+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14+36, mixed mode, sharing)

 

JEP 364: ZGC on macOS et JEP 365: ZGC on Windows

Le portage de ZGC sous MacOS et Windows 10 et Server version 1803 minimum est proposé en expérimental. Il faut donc activer l’utilisation des fonctionnalités expérimentales dans la JVM avec l’option -XX:+UnlockExperimentalVMOptions. Attention à l’ordre d’utilisation de cette option.

C:\java>java -XX:+UseZGC -version
Error: VM option 'UseZGC' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions.
Error: The unlock option must precede 'UseZGC'.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

 

C:\java>java -XX:+UseZGC -XX:+UnlockExperimentalVMOptions  -version
Error: VM option 'UseZGC' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions.
Error: The unlock option must precede 'UseZGC'.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

 

C:\java>java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -version
openjdk version "14" 2020-03-17
OpenJDK Runtime Environment AdoptOpenJDK (build 14+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14+36, mixed mode)

Note : ZGC et Shenandoah sont proposés pour devenir standard dans Java 15.

 

JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination

L’utilisation de cette combinaison de ramasse-miettes est dépréciée : son utilisation affiche un avertissement au lancement de la JVM

C:\java>java -XX:+UseParallelGC -XX:-UseParallelOldGC -version
OpenJDK 64-Bit Server VM warning: Option UseParallelOldGC was deprecated in version 14.0 and will likely be removed in a future release.
openjdk version "14" 2020-03-17
OpenJDK Runtime Environment AdoptOpenJDK (build 14+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14+36, mixed mode, sharing)

 

JEP 352: Non-Volatile Mapped Byte Buffers

Cette JEP permet au JDK d’utiliser la classe FileChannel pour créer des MappedByteBuffer qui permettent un accès à de la Non-Volatile Memory (NVM).

Un nouveau module jdk.nio.mapmode contient les MapMode READ_ONLY_SYNC et READ_WRITE_SYNC définis dans la classe ExtendedMapMode.

Une nouvelle méthode void writebackMemory(long address, long length) est ajoutée à la classe jdk.internal.misc.Unsafe

Cette fonctionnalité n’est utilisable par le JDK que sous Linux avec des architectures x64 et AArch64.

 

Les fonctionnalités concernant les outils

 

JEP 343: Packaging Tool (Incubator)

Initialement proposée pour Java 13, cette JEP est ajoutée en Java 14. Un nouvel outil nommé jpackage est proposé dans le JDK pour créer des installeurs natifs pour applications Java reposant sur les fonctionnalités fournies par les systèmes d’exploitation :

  • Windows : msi et exe
  • Linux : deb et rpm
  • MacOS : pkg et dmg

Cela permet de facilement installer et désinstaller une application sur un système d’exploitation supporté.

Jpackage utilise une image qu’il va créer en utilisant jlink ou une image créée préalablement avec jlink notamment pour une application non modulaire.

Attention : cette fonctionnalité est proposée sous la forme d’un module incubator.

 

JEP 367: Remove the Pack200 Tools and API

Les outils pack200 et unpack200, ainsi que l’API Pack200 dans le package java.util.jar, dépréciés en Java 11 via la JEP 336 sont retirés.

 

Conclusion

En plus des JEPs, Java 14 apporte aussi des corrections et quelques évolutions mineures dans certaines API.

Java 14 n’est pas LTS et sera obsolète avec la sortie de Java 15 au mois de septembre prochain. Cette version permet aux développeurs de se familiariser avec les fonctionnalités standard qui seront donc dans Java 17 (la prochaine version LTS) et fournir du feed back sur les fonctionnalités en preview et en incubator.

Le prochain article de cette série détaille les records, un nouveau type ajouté au langage Java.