Si une application existante est exécutée avec Java 19, il est possible de voir des points d'interrogation sur la console au lieu de caractères spéciaux. Cela est dû au fait que, depuis Java 19, l'encodage par défaut du système d'exploitation est utilisé pour l'impression dans System.out et System.err par exemple, "Cp1252" sous Windows.
Pour changer la sortie en UTF-8, vous devez ajouter les options VM suivantes lorsque vous appelez l'application :
-Dstdout.encoding=utf8 -Dstderr.encoding=utf8
Si vous ne voulez pas faire cela à chaque fois que vous démarrez le programme, vous pouvez également définir ces paramètres de manière globale en définissant la variable d'environnement suivante :
_JAVA_OPTIONS="-Dstdout.encoding=utf8 -Dstderr.encoding=utf8"
Nouvelles méthodes pour créer des HashMaps préalloués
Si nous voulons créer une ArrayList pour un nombre connu d'éléments (par exemple, 120), nous pouvons le faire comme suit depuis toujours :
List<String> list = new ArrayList<>(120);
Ainsi, le tableau sous-jacent à ArrayList est alloué directement pour 120 éléments et ne doit pas être agrandi plusieurs fois (c'est-à-dire nouvellement créé et copié) pour insérer les 120 éléments. De même, nous avons toujours été en mesure de générer un HashMap comme suit :
Map<String, Integer> map = new HashMap<>(120);
« Intuitivement, on pourrait penser que cette HashMap offre de l'espace pour 120 mappings. Ce qui n'est pas le cas », précise Sven Woltmann, développeur Java. Le HashMap est initialisé avec un facteur de charge par défaut de 0,75. Cela signifie que dès que le HashMap est rempli à 75 %, il est reconstruit ("rehashed") avec une taille double. Cela permet de s'assurer que les éléments sont répartis aussi uniformément que possible dans les buckets de la HashMap et que le moins de buckets possible contiennent plus d'un élément.
Ainsi, le HashMap initialisé avec une capacité de 120 ne peut contenir que 120 × 0,75 = 90 mappings. Pour créer un HashMap pour 120 mappings, vous avez dû calculer la capacité en divisant le nombre de mappings par le facteur de charge : 120 ÷ 0,75 = 160. Il fallait donc créer un HashMap pour 120 mappings comme suit :
Code : | Sélectionner tout |
1 2 | // for 120 mappings: 120 / 0.75 = 160 Map<String, Integer> map = new HashMap<>(160); |
Java 19 nous facilite la tâche ; nous pouvons maintenant écrire ce qui suit à la place :
Map<String, Integer> map = HashMap.newHashMap(120);
Si nous regardons le code source des nouvelles méthodes, nous constatons qu'elles font la même chose que précédemment :
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | public static <K, V> HashMap<K, V> newHashMap(int numMappings) { return new HashMap<>(calculateHashMapCapacity(numMappings)); } static final float DEFAULT_LOAD_FACTOR = 0.75f; static int calculateHashMapCapacity(int numMappings) { return (int) Math.ceil(numMappings / (double) DEFAULT_LOAD_FACTOR); } |
La méthode newHashMap() a également été ajoutée à LinkedHashMap et WeakHashMap.
La concurrence structurée, en phase d'incubation, est destinée à simplifier la programmation multithread grâce à une API de concurrence structurée. Cette concurrence traite plusieurs tâches exécutées dans différents threads comme une seule unité de travail, afin de rationaliser la gestion des erreurs et l'annulation. La fiabilité et l'observabilité sont améliorées. Pour ceux qui ont besoin d'accéder à du code non-Java (par exemple, la bibliothèque standard C), il y a aussi de bonnes nouvelles : l'API Foreign Function & Memory a atteint le stade de l'aperçu après cinq cycles d'incubation.
Si une tâche se compose de différentes sous-tâches qui peuvent être exécutées en parallèle (par exemple, l'accès aux données d'une base de données, l'appel d'une API distante et le chargement d'un fichier), nous pouvions jusqu'à présent utiliser le framework d'exécution Java pour cela. Cela pourrait alors ressembler à ceci, par exemple :
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | private final ExecutorService executor = Executors.newCachedThreadPool(); public Invoice createInvoice(int orderId, int customerId, String language) throws ExecutionException, InterruptedException { Future<Order> orderFuture = executor.submit(() -> loadOrderFromOrderService(orderId)); Future<Customer> customerFuture = executor.submit(() -> loadCustomerFromDatabase(customerId)); Future<String> invoiceTemplateFuture = executor.submit(() -> loadInvoiceTemplateFromFile(language)); Order order = orderFuture.get(); Customer customer = customerFuture.get(); String invoiceTemplate = invoiceTemplateFuture.get(); return Invoice.generate(order, customer, invoiceTemplate); } |
En utilisant un StructuredTaskScope, nous pouvons réécrire l'exemple comme suit :
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Invoice createInvoice(int orderId, int customerId, String language) throws ExecutionException, InterruptedException { try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { Future<Order> orderFuture = scope.fork(() -> loadOrderFromOrderService(orderId)); Future<Customer> customerFuture = scope.fork(() -> loadCustomerFromDatabase(customerId)); Future<String> invoiceTemplateFuture = scope.fork(() -> loadInvoiceTemplateFromFile(language)); scope.join(); scope.throwIfFailed(); Order order = orderFuture.resultNow(); Customer customer = customerFuture.resultNow(); String invoiceTemplate = invoiceTemplateFuture.resultNow(); return new Invoice(order, customer, invoiceTemplate); } } |
ExecutorService est remplacé dans le scope de la classe par un StructuredTaskScope situé dans le scope de la méthode - et [C=Java]executor.submit() par scope.fork(). En utilisant scope.join(), nous attendons que toutes les tâches soient terminées ou qu'au moins une d'entre elles échoue ou soit annulée. Dans ces deux derniers cas, la méthode throwIfFailed() qui suit lance une ExecutionException ou une CancellationException.
Un aperçu d'une fonction étrangère et d'une API de mémoire, qui introduirait une API par laquelle les programmes Java peuvent interagir avec du code et des données en dehors du runtime Java. En invoquant efficacement des fonctions étrangères (c'est-à-dire du code extérieur à la JVM) et en accédant en toute sécurité à la mémoire étrangère (c'est-à-dire la mémoire non gérée par la JVM), l'API permet aux programmes Java d'appeler des bibliothèques natives et de traiter des données natives sans le danger et la fragilité de l'interface Java Native (JNI).
L'API de fonction et de mémoire étrangères combine deux API antérieures en incubation : l'API d'accès à la mémoire étrangère et l'API de linker étrangère. L'API de fonction et de mémoire étrangères a été incubée dans le JDK 17 et réincubée dans le JDK 18. Les objectifs de la proposition comprennent la facilité d'utilisation, les performances, la généralité et la sécurité. Voici un exemple simple qui stocke une chaîne de caractères dans la mémoire off-heap et appelle la fonction strlen de la bibliothèque standard du C sur celle-ci :
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class FFMTest { public static void main(String[] args) throws Throwable { // 1. Get a lookup object for commonly used libraries SymbolLookup stdlib = Linker.nativeLinker().defaultLookup(); // 2. Get a handle to the "strlen" function in the C standard library MethodHandle strlen = Linker.nativeLinker().downcallHandle( stdlib.lookup("strlen").orElseThrow(), FunctionDescriptor.of(JAVA_LONG, ADDRESS)); // 3. Convert Java String to C string and store it in off-heap memory MemorySegment str = implicitAllocator().allocateUtf8String("Happy Coding!"); // 4. Invoke the foreign function long len = (long) strlen.invoke(str); System.out.println("len = " + len); } } |
La FunctionDescriptor de la ligne 9 est intéressant : il attend comme premier paramètre le type de retour de la fonction et comme paramètres supplémentaires les arguments de la fonction. La FunctionDescriptor garantit que tous les types Java sont correctement convertis en types C et vice versa.
Une Preview des modèles d'enregistrement, pour déconstruire les valeurs d'enregistrement. Les modèles d'enregistrement et les modèles de type peuvent être imbriqués pour permettre une forme déclarative, puissante et composable de navigation et de traitement des données. Les objectifs de la proposition comprennent l'extension du filtrage pour exprimer des requêtes de données plus sophistiquées et composables, sans modifier la syntaxe ou la sémantique des patrons de type. Cette proposition s'appuie sur le filtrage de motifs pour instanceof, livré dans le JDK 16 en mars 2021.
Les plans futurs peuvent prévoir l'extension des modèles d'enregistrement avec des capacités telles que les modèles de tableaux et les modèles de variables. Les modèles d'enregistrement font partie du projet Amber, un effort visant à explorer et à incuber des fonctionnalités Java plus petites et orientées vers la productivité.
Source : Java 19
Et vous ?
Quel est votre avis sur le sujet ?
Les exemples présentés par le développeur Sven Woltmann sont-ils pertinents ?
Voir aussi :
JDK 19 : les nouvelles fonctionnalités de Java 19 incluent la concurrence structurée, les modèles d'enregistrement et l'aperçu d'une API de fonction et de mémoire étrangères
JDK 18, l'implémentation de référence de Java 18, est désormais disponible, elle propose comme Python, Ruby, PHP ou encore Erlang, un mini serveur Web prêt à l'emploi
La version 2022.1 d'IntelliJ IDEA va prendre en charge les fonctionnalités de Java 18 : un aperçu des mises à jour pour le langage
Les contrôleurs de conformité d'Oracle incluraient désormais Java dans les audits de licence, les abonnements payants introduits il y a trois ans retiennent désormais l'attention de l'équipe