Les nouveautés de Java 9
Pour les développeurs
Loïc Mathieu - @loicmathieu
JPMS
Project Jigsaw
Les JEPs
JEP 102: Process API Updates
- But : faciliter l'appel à un programme (process) externe depuis Java
- Ajout de nombreuses fonctionnalités à l'API actuelle :
- récupérer le PID,
- killer un process
- récupérer la ligne de commande
- …
- Ajout de ProcessHandle qui permet de gérer le process de la JVM (ProcessHandle.current()) ou un process fils (Runtime.getRuntime().exec(""))
- Compatible avec les différents OS supportés par Java
JEP 102: Process API Updates
// Get PIDs of own processes
System.out.println("Your pid is " + ProcessHandle.current().getPid());
//start a new process and get PID
Process p = Runtime.getRuntime().exec("sleep 1h");
ProcessHandle h = ProcessHandle.of(p.getPid()).orElseThrow(IllegalStateException::new);
// Do things on exiting process : CompletableFuture !
h.onExit().thenRun( () -> System.out.println("Sleeper exited") );
// Get info on process : return Optional!
System.out.printf("[%d] %s - %s\n", h.getPid(), h.info().user().orElse("unknown"),
h.info().commandLine().orElse("none"));
// Kill a process
h.destroy();
JEP 110: HTTP/2 Client (Incubator)
-
HTTP/2 est là, et beaucoup de site l’exploitent déjà.
-
Une nouvelle API a été créée pour proposer une implémentation plus simple à utiliser que la très ancienne HttpURLConnection
-
Nouveau client HTTP au goût du jour, synchrone ou asynchrone (alors basé sur les CompletableFuture).
- Disponible dans le module "incubator"
JEP 110: HTTP/2 Client (Incubator)
/**
* The HTTP API functions asynchronously and synchronously.
* In asynchronous mode, work is done in threads (ExecutorService).
*/
public static void main(String[] args) throws Exception {
HttpClient.getDefault()
.request(URI.create("http://www.loicmathieu.fr"))
.GET()
.responseAsync() // CompletableFuture
.thenAccept(httpResponse ->
System.out.println(httpResponse.body(HttpResponse.asString()))
);
Thread.sleep(999); // Give worker thread some time.
}
JEP 259: Stack-Walking API
Stack-Walking API : une nouvelle API pour parcourir une stack d'execution.
JEP 259: Stack-Walking API
// return class/method only for our classes.
private static List<String> walkAndFilterStackframe() {
return StackWalker.getInstance().walk(s ->
s.map( frame -> frame.getClassName() + "/" + frame.getMethodName())
.filter(name -> name.startsWith("fr.loicmathieu"))
.limit(10)
.collect(Collectors.toList()) );
}
JEP 269: Convenience Factory Methods for Collections
-
L'un des plus grand changement en terme d’API : des méthodes statiques pour la création de collection immuable.
-
Cible : création de petite collections (list, set, map).
- La performance et l’utilisation mémoire ont été au centre de l’implémentation de ces collections.
JEP 269: Convenience Factory Methods for Collections
List<Integer> listOfNumbers = List.of(1, 2, 3, 4, 5);
Set<Integer> setOfNumbers = Set.of(1, 2, 3, 4, 5);
Map<String, String> mapOfString =
Map.of("key1", "value1", "key2", "value2");
Map<String, String> moreMapOfString =
Map.ofEntries(
Map.entry("key1", "value1"),
Map.entry("key2", "value2"),
Map.entry("key1", "value3")
);
JEP 266: More Concurrency Updates
Implémentation des Reactive Stream en Java via la classe Flow :
- Publisher : Produit des messages que les subscriber vont consommer. La seule méthode est subscribe(Subscriber).
- Subscriber : Souscrit a un publisher pour recevoir des messages (via la methode onNext(T)), des messages d’erreur (onError(Throwable)), ou un signal comme quoi il n’y aura plus de messages (onComplete()). Avant toute chose, le publisher doit appeler onSubscription(Subscription).
- Subscription : La connexion entre un publisher et un subscriber. Le subscriber va l’utiliser pour demander des messages (request(long)) ou pour rompre la connexion (cancel()).
Flow
JEP 266: More Concurrency Updates
public class MySubscriber<T> implements Subscriber<T> {
private Subscription subscription;
@Override
public void onSubscribe(Subscription subscription) {
this.subscription = subscription;
subscription.request(1);
}
@Override
public void onNext(T item) {
System.out.println("Got : " + item);
subscription.request(1);
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
}
@Override
public void onComplete() {
System.out.println("Done");
}
}
Flow
JEP 266: More Concurrency Updates
//Create Publisher
SubmissionPublisher<String> publisher = new SubmissionPublisher<>();
//Register Subscriber
MySubscriber<String> subscriber = new MySubscriber<>();
publisher.subscribe(subscriber);
//Publish items
System.out.println("Publishing Items...");
String[] items = {"1", "x", "2", "x", "3", "x"};
Arrays.asList(items).stream().forEach(i -> publisher.submit(i));
publisher.close();
Flow
JEP 266: More Concurrency Updates
Quelques changements à l’API CompletableFuture ont vu le jour permettant, entre autre, une meilleur composition des CompletableFuture entre elles :
-
copy():CompletableFuture<T>
-
completeAsync(Supplier<? extends T> supplier):CompletableFuture<T>
-
orTimeout(long timeout,TimeUnit unit):CompletableFuture<T>
-
completeOnTimeout(T value, long timeout, TimeUnit unit):CompletableFuture<T>
-
failedFuture(Throwable ex):CompletableFuture<T>
CompletableFuture
JEP 277: Enhanced Deprecation
Ajout de deux attributs à l'annotation @Deprecated :
- since : depuis quelle version l'API est dépréciée.
- forRemoval : mettre à true (par défaut false) si l'API sera supprimée dans une future release.
Le but est de faciliter le cycle de vie des applications et de permettre, peut-être plus sereinement, de supprimer dans le futur certaines API du JDK lui-même.
JEP 277: Enhanced Deprecation
@Deprecated(since="9", forRemoval=true)
public class MyDeprecatedClass {
//deprecated stuff
}
JEP 222: jshell: The Java Shell (Read-Eval-Print Loop)
Beaucoup de languages (ruby, scala, python, …) proposent une Read-Evaluate-Print-Loop (REPL), un shell.
Celà permet un apprentissage aisé du language et donne une porte d’entré direct depuis un simple shell.
Eviter le cérémonial d’édition, de compilation, de packaging du code.
Avec Java 9, apparait jshell, le REPL de java !
Depuis une ligne de commande, exécutez <java_home>/bin/jshell et laissez vous guider par l’aide (/help). Exemple ci-dessous :
JEP 222: jshell: The Java Shell (Read-Eval-Print Loop)
JEP 213: Milling Project Coin
Project Coin est un projet d’évolution du language, qui a débuté avec java 7 dans le but de simplifier l’utilisation du language pour les développeurs en apportant des petites modifications de l’ordre du « sucre syntaxique ».
La JEP213 : Milling Project Coin contient l’implémentation dans Java 9 des dernières partie du projet.
JEP 213: Milling Project Coin
-
@SafeVarargs autorisé sur une méthode privé (précédemment uniquement dans les méthodes static ou finale)
- Autorisation de l’opérateur <> pour les classes abstraite (quand le type est dénotable)
- Suppression de ‘_’ comme un identificateur valide pour permettre sa réutilisation en Java 10
- Méthode privé dans les interfaces : permet la factorisation de code de méthode statique dans une même interface.
JEP 213: Milling Project Coin
Avant Java 9 :
final Resource r = new Resource();
try (Resource r2 = r) { … }
Après Java 9 :
final Resource r = new Resource();
try (r) { … // Cannot mutate r }
- Autorisation des variables finale (ou effectivement finale) dans un try-with-resource (exemple ci-dessous) :
Pleins d’autres changements hors JEP
Stream
- Stream.takeWhile(Predicate<? super T> predicate):Stream<T> : construit une stream qui contient les éléments de la première tant que le prédicat est vrai. Dès que le prédicat devient faux la stream est coupée.
- Stream.dropWhile(Predicate<? super T> predicate):Stream<T> : l’inverse de takeWhile, construit une stream qui contient le premier élément faux puis les suivants. Tant que le prédicat est faux : supprime les émélments, dès qu’il est vrai, inclue les éléments dans la stream.
- Stream.ofNullable(T element):Stream<T> : retourne une stream avec l’élément ou une stream vide si l’élément est nulle. Evite l’utilisation d’Optional avec les stream
- Stream.iterate(T, Predicate<? super T>, UnaryOperator<T>) : réplique une boucle for standard : Stream.iterate(0; i -> i<10, i -> i+1)
Stream
//iterate
/java 8 style : using for loop
for (int i = 0; i < 10; ++i) {
System.out.println(i);
}
//java 9 style, using Stream.iterate
Stream.iterate(0, i -> i < 10, i -> i + 1).forEach(System.out::println);
//takeWhile and dropWhile
Stream<String> stream = Stream.iterate("", s -> s + "s")
stream.takeWhile(s -> s.length() < 10);
stream.dropWhile(s -> !s.contains("sssss"));
//ofNullable : returning Stream.empty() for null element
//java 8 style : we need to make a check to know if it's null or not
collection.stream()
.flatMap(s -> {
Integer temp = map.get(s);
return temp != null ? Stream.of(temp) : Stream.empty();
})
.collect(Collectors.toList());
//java 9 style
collection.stream().flatMap(s -> Stream.ofNullable(map.get(s))).collect(Collectors.toList());
Collectors
-
Collectors.filtering(Predicate<? super T>,Collector<? super T,A,R>) : execute un filtre en amont du collector (voir un exemple dans la Javadoc qui explique la différence avec l’utilisation de Stream.filter() avant l’utilisation d’un collector)
- Collectors.flatMapping(Function<? super T,? extends Stream<? extends U>>,Collector<? super U,A,R>) : execute une operation de type flatMap en amont du collector (voir un exemple concret dans la Javadoc)
Collectors
List<Integer> numbers = List.of(1, 2, 3, 5, 5);
Map<Integer, Long> result = numbers.stream()
.filter(val -> val > 3)
.collect(Collectors.groupingBy(i ->; i, Collectors.counting()));
result = numbers.stream()
.collect(Collectors.groupingBy(i -> i,
Collectors.filtering(val -> val > 3, Collectors.counting())
));
Optional
4 nouvelles méthodes ont été ajoutée à la classe Optional :
- or(Supplier):Optional : retourne le même Optional ou un Optional construit avec le Supplier passé en paramètre si il n’y a pas de valeur. Cela permet une construction lazy d’un autre Optional si la valeur du premier n’existe pas.
- ifPresent(Consumer):void : execute le Consumer en paramètre s’il y a une valeur de présente
- ifPresentOrElse(Consumer, Runnable):void : execute le Consumer en paramètre s’il y a une valeur de présente sinon execute le Runnable
- stream():Stream<T> : retourne un Stream d’un element s’il y a une valeur ou un Stream vide. Cela permet l’utilisation de l’API Stream avec les Optional.
Optional
//Optional.or : a lazy version of orElse
Optional<String> value = ...;
Optional<String> defaultValue = Optional.of(() -> bigComputation());
return value.or(defaultValue);//bigComputation will be called only if value is empty
//Optional.ifPresent :
Optional<String> value = ...;
AtomicInteger successCounter = new AtomicInteger(0);
value.ifPresent(
v -> successCounter.incrementAndGet());
//Optional.ifPresentOrElse :
Optional<String> value = ...;
AtomicInteger successCounter = new AtomicInteger(0);
AtomicInteger onEmptyOptionalCounter = new AtomicInteger(0);
value.ifPresentOrElse(
v -> successCounter.incrementAndGet(),
onEmptyOptionalCounter::incrementAndGet);
//Optional.stream : unify the stream and Optional API
Optional<String> value = Optional.of("a");
List<String> collect = value.stream().map(String::toUpperCase).collect(Collectors.toList());
//["A"]
Java Time
Plusieurs ajouts à l’API Java Time, entre autre la possibilité de créer un Stream de date avec :
-
LocalDate.datesUntil(LocalDate)
-
LocalDate.datesUntil(LocalDate,Period)
InputStream
-
InputStream.readAllBytes():byte[] : lit d’un seul coup un input stream en tableau de byte
- InputStream.readNBytes(byte[] b, int off, int len):int : lit d’un seul coup un input stream dans le tableau de byte en paramètre avec offset et limite.
Objects
- Objects.requireNonNullElse(T obj, T defaultObj) : retourne le premier éléments si non null, sinon le deuxième. Si les deux sont null : NullPointerException !
- Objects.requireNonNullElseGet(T obj, Supplier supplier) : retourne le premier éléments si non null, sinon appel le supplier.get().
- int Objects.checkIndex(int index, int length) : check de l’index : génère un IndexOutOfBoundsException si l’index est inférieur à 0 ou supérieur ou éagle à la taille. Cette méthode peut-être optimisée par le JVM.
- int Objects.checkFromToIndex(int fromIndex, int toIndex, int length) : idem mais pour le sous-range fromIndex/toIndex
- int Objects.checkFromIndexSize(int fromIndex, int size, int length) : idem mais pour le sous-range fromIndex/fromIndex + size
Math, StrictMath, BigDecimal, BigInteger
-
{Math, StrictMath}.fma() : implémentation de l’opération fused-multiply-accumulate (fma)
-
{Math, StrictMath}.{multiplyExact, floorDiv, floorMod}
- {BigDecimal, BigInteger}. sqrt() : racine carré
Arrays
Beaucoup de nouvelles méthodes permettant de comparer des tableaux :
-
Arrays.equals()
-
Arrays.compare()
-
Arrays.compareUnsigned()
-
Arrays.mismatch()
Et plus encore
Performance
Voici une liste de JEP de Java 9 axés performances :
- JEP 143: Improve Contended Locking : optimisation des monitors Java (optimisation des lock).
- JEP 193: Variable Handles : C++ atomics …
- JEP 197: Segmented Code Cache : le code cache (une partie du Metaspace) a été séparé pour en optimiser les performances.
- JEP 274: Enhanced Method Handles : plusieurs ajout à l’API MethodHandle
- JEP 285: Spin-Wait Hints : pour les utilisateurs bas niveau uniquement : possibilité de préciser à la JVM qu’on est dans une spin-wait-loop …
Performance
-
JEP 254: Compact Strings : revue de l’implémentation des String en Java pour en proposer une version plus compact en cas de Sring ISO-8859-1 (ou Latin-1).
- UTF16 : chaque caractère est stocké sur deux octets.
- ISO-8859-1 chaque caractère est stocké sur un octets.
- JEP 280: Indify String Concatenation : intrasification de la concaténation des chaînes de caractère dans le JVM.
Démarrage
Beaucoup de travail a été fait pour optimiser le démarrage de Java en terme de temps de démarrage de la JVM et d'empreinte mémoire mené par Claes Redestad (Oracle).
L'implémentation de la modularisation a potentiellement ralenti le démarrage de java 9 : attention test réalisé sur une version non définitive de Java 9 !
Démarrage
Des tests réalisés en comparaison d'un Java 9EAb176 et d'un Java 8u144 ont montré que :
- Java démarre très vite (<100ms pour un HelloWorld)
- Java démarre plus lentement (2.5x) quand utilisation d'une lambda et nécessite 2Mo en plus
- En java 9 : la différence entre utilisation ou pas de lambda se réduit (et c'est presque plus rapide qu'en java 8)
- Java 9 démarre plus lentement que Java 8 et prend plus de mémoire
- Lenteur : Est-ce la cause de JPMS ?
- Mémoire : G1 par défaut en est la cause
Démarrage
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
public class HelloWorldLambda {
public static void main(String[] args) {
Runnable hello = () -> System.out.println("Hello lambda world");
hello.run();
}
}
Démarrage
Les tests en détails : http://www.loicmathieu.fr/wordpress/informatique/demarrage-jvm-8-vs-9/
time java HelloWorld
real 0m0.085s
user 0m0.078s
sys 0m0.019s
time java HelloWorldLambda
real 0m0.185s
user 0m0.236s
sys 0m0.032s
java -XX:+UnlockDiagnosticVMOptions \
-XX:NativeMemoryTracking=summary \
-XX:+PrintNMTStatistics \
-Xmx16m -Xms16m HelloWorld
Total: reserved=1356124KB, committed=57156KB
java -XX:+UnlockDiagnosticVMOptions \
-XX:NativeMemoryTracking=summary \
-XX:+PrintNMTStatistics \
-Xmx16m -Xms16m HelloWorldLambda
Total: reserved=1358869KB, committed=59901KB
time java HelloWorld
real 0m0.161s
user 0m0.177s
sys 0m0.048s
time java HelloWorldLambda
real 0m0.199s
user 0m0.228s
sys 0m0.042s
java -XX:+UnlockDiagnosticVMOptions \
-XX:NativeMemoryTracking=summary \
-XX:+PrintNMTStatistics \
-Xmx16m -Xms16m HelloWorld
Total: reserved=1389612KB, committed=97536KB
java -XX:+UnlockDiagnosticVMOptions \
-XX:NativeMemoryTracking=summary \
-XX:+PrintNMTStatistics \
-Xmx16m -Xms16m HelloWorldLambda
Total: reserved=1390008KB, committed=98188KB
Crédits
Cette présentation est inspirée de l'article :
http://www.loicmathieu.fr/wordpress/informatique/les-nouveautes-de-java-9-pour-les-developeurs/
Cet article est lui-même inspiré de :
https://blogs.oracle.com/darcy/resource/Devoxx/DevoxxUS-2017-jdk9-lang-tools-libs.pdf
http://docs.oracle.com/javase/9/whatsnew/toc.htm#JSNEW-GUID-BA9D8AF6-E706-4327-8909-F6747B8F35C5
http://blog.takipi.com/5-features-in-java-9-that-will-change-how-you-develop-software-and-2-that-wont/
https://bentolor.github.io/java9-in-action
http://www.javaworld.com/article/2598480/core-java/why-developers-should-get-excited-about-java-9.html
https://www.sitepoint.com/ultimate-guide-to-java-9/
http://www.javamagazine.mozaicreader.com/JulyAug2017
Merci !
&
Question ?
Loïc Mathieu - @loicmathieu
Java 9
By loicmathieu
Java 9
- 1,418