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 :

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

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

Merci !

&

Question ?

Loïc Mathieu -        @loicmathieu

Made with Slides.com