I. La modularisation via le projet Jigsaw▲
La modularisation annoncée depuis de nombreuses années, plusieurs fois repoussée, est LA grande nouveauté de Java 9. Elle a été décrite principalement dans la JEP 201. D'autres JEP sont liées à celle-ci tant le projet de modularisation est importante. L'objectif principal du système des modules est de fournir un JDK qui puisse être structuré et de pouvoir charger seulement les modules nécessaires. Les applications du domaine de l'Internet des Objets (IOT en anglais) sont très demandeuses, car les systèmes hôtes sont généralement limités en ressources matérielles (mémoire, CPU…). Les développeurs Java pourront donc spécifier les modules du JDK et des bibliothèques tierces qui sont requis par leur application.
Sur le plan théorique, le système de module apporte également de nombreux autres avantages :
- une plateforme plus facilement évolutive et maintenable ;
- des gains en termes de performance et d'exécution puisque seuls les modules nécessaires sont chargés ;
- des gains en termes de sécurité, car le principe modulaire permet de fournir le juste assez nécessaire au système. De plus, l'utilisation d'API privées est impossible si celles-ci ne sont pas explicitement exportées.
I-A. Comment Jigsaw fonctionne ?▲
Pour considérer une application tirant parti du système de modules, un fichier module-info.java est requis à la racine de votre projet. Un exemple de contenu de fichier module-info.java est proposé ci-dessous :
2.
3.
4.
5.
module
fr.developpez.com {
requires
java.sql;
export
fr.developpez.com.services;
}
Nous remarquons l'usage de nouveaux mots clés comme module, requires et export. Le premier étant utilisé pour nommer le module, le deuxième pour exprimer les modules nécessaires et le troisième pour indiquer les packages du module qui seront visibles à l'extérieur. Pour le dernier point, les packages souvent nommés impl pourront désormais être vraiment considérés comme privés.
Les personnes ayant une connaissance sur OSGi ne seront pas surprises par la syntaxe hormis l'absence de numéro de version. En effet dans cette première version, Jigsaw a fait l'impasse sur les versions. Cet aspect est délégué aux outils de build comme Maven ou Gradle.
Bien sûr, comme toute grosse nouveauté, il y a quelques problèmes. On peut citer par exemple l'utilisation de la méthode setAccessible(boolean) qui permet d'accéder à des champs privés par réflexion. Cette méthode ne fonctionnera plus pour accéder à un champ privé contenu dans un module pour des raisons de sécurité et vous obtiendrez donc une belle exception de type : IllegalAccessError. Pour résoudre cela, il faudra « ouvrir » le module en utilisant le mot clé open sur le package ou plus globalement sur le module (voir ci-dessous).
2.
3.
4.
module
fr.developpez.com {
requires
java.sql;
opens
fr.developpez.com.services;
}
Ou
2.
3.
4.
open
module
fr.developpez.com {
requires
java.sql;
exports
fr.developpez.com.services;
}
I-B. Les outils du JDK▲
Au niveau des outils il y a eu également des changements. Les commandes java et javac disposent naturellement de paramètres spécifiques pour prendre en compte le système de module.
L'outil jlinkJEP 282 permet d'assembler plusieurs modules Java entre eux en tenant compte de leurs dépendances. Le résultat consiste en une image spécifique du runtime Java (JDK ou JRE) en intégrant son application.
Enfin, sans être exhaustif, l'outil jdeps permet de connaître les modules dont dépend votre projet.
I-C. Et Eclipse comment se comporte-il ?▲
TODO
II. Fabriques pour les collections▲
La nouvelle version de Java 9 apporte de gros changements au niveau des API dédiées aux collections. Des méthodes statiques ont été ajoutées sur les interfaces de List, Set et Map pour initialiser des collections. Par ailleurs, les objets créés sont immutables (les attributs les contenant ne sont pas modifiables). L'objectif visé par cette amélioration décrite dans la JEP 269 étant bien sûr la création de petites collections et d'éviter une lourdeur syntaxique.
Avant Java 9, nous étions obligés de faire cela :
2.
3.
4.
5.
List<
String>
myList =
new
ArrayList<
String>
(
);
myList.add
(
"
Developpez
.
com
"
);
myList.add
(
"
Aime
"
);
myList.add
(
"
Java
"
);
myList =
Collections.unmodifiableSet
(
myList);
Il y avait bien sur l'exception de la classe Arrays pour créer des listes toutes prêtes :
List<
String>
myList =
Arrays.asList
(
&
#8220
;Developpez.com&
#8221
;, &
#8220
;Aime&
#8221
;, &
#8220
;Java&
#8221
;)
Malheureusement, il faut bien avouer qu'il n'est pas intuitif de faire appel à la classe Arrays pour créer des listes. Par ailleurs pour Set et Map, il n'y avait pas d'équivalent à la classe Arrays.
Avec Java 9, la création devient plus simple.
Pour créer une instance de List :
List<
String>
newList =
List.of
(
"
Developpez
.
com
"
, "
Aime
"
, "
Java
"
)
Pour créer une instance de Set :
Set<
String>
newSet =
Set.of
(
&
#8220
;Developpez.com&
#8221
;,&
#8221
;Aime&
#8221
;, &
#8220
;Java&
#8221
;)
Pour créer une instance de Map :
Map<
String,String>
newMap =
Map.of
(
&
#8220
;Cle1&
#8221
;, &
#8220
;Developpez.com&
#8221
;, &
#8220
;Cle2&
#8221
;, &
#8220
;Aime&
#8221
;, &
#8220
;Cle3&
#8221
;, &
#8220
;Java&
#8221
;)
Les objets créés sont immutables. Par conséquent si vous essayez d'ajouter du contenu aux précédentes collections, vous obtiendrez une belle exception de type UnsupportedOperationException.
2.
3.
4.
5.
newList.add
(
"
Vive
Java
9
"
)
|
java.lang.UnsupportedOperationException thrown:
|
at ImmutableCollections.uoe (
ImmutableCollections.java:69
)
|
at ImmutableCollections$AbstractImmutableList.add
(
ImmutableCollections.java:75
)
|
at (
#33
:1
)
III. API pour la gestion des processus▲
Cette amélioration, décrite dans la JEP 102 permet à Java de mieux coexister avec le système. Initialement le développeur Java utilisait l'API Runtime.getRuntime().exec() puis avec Java 5 est apparu l'API ProcessBuilder. Malheureusement, il n'était pas possible de connaître le PID du processus courant, connaître les sous-processus, détruire des processus ou obtenir d'autres informations comme les paramètres d'exécution.
Avec Java 9, les choses ont évolué dans le bon sens avec des nouvelles classes comme ProcessHandle et des ajouts dans la classe Process.
Pour récupérer le processus courant :
2.
jshell>
System.out.println
(
ProcessHandle.current
(
))
45
Pour créer un processus (rien de nouveau) :
jshell>
Process pr =
Runtime.getRuntime
(
).exec
(
"
sleep
1h
"
);
Pour afficher le PID et les informations de la ligne de commande de tous les processus :
2.
3.
4.
jshell>
ProcessHandle.allProcesses
(
).forEach
(
p -
>
System.out.println
(
p.getPid
(
) +
"
"
+
p.info
(
).commandLine
(
)));
1
Optional[/
usr/
lib/
jvm/
java-
9
-
openjdk-
amd64/
bin/
jshell -
v]
45
Optional[/
usr/
lib/
jvm/
java-
9
-
openjdk-
amd64/
bin/
java -
agentlib:jdwp=
transport=
dt_socket,address=
localhost:33801
jdk.jshell.execution.RemoteExecutionControl 34065
]
137
Optional[/
usr/
bin/
sleep 1
h]
Pour afficher le PID et les informations de la ligne de commande des sous processus du processus courant :
2.
jshell>
ProcessHandle.current
(
).children
(
).forEach
(
p -
>
System.out.println
(
p.getPid
(
) +
"
"
+
p.info
(
).commandLine
(
)))
137
Optional[/
usr/
bin/
sleep 1
h]
Pour créer un ProcessHandle à partir d'un numéro de PID :
jshell>
ProcessHandle.of
(
137
).get
(
).info
(
).commandLine
(
).get
(
)
Pour exécuter du code à la fin du processus :
jshell>
ProcessHandle.of
(
137
).get
(
).onExit
(
).thenRun
(
(
) -
>
System.out.println
(
"
Commande
sleep
arrêtée
"
) );
Pour supprimer un processus à partir d'un PID :
2.
jshell>
ProcessHandle.of
(
137
).get
(
).destroy
(
)
Commande sleep arrêtée
IV. Multi-release des fichiers JAR▲
Le format du fichier JAR évolue. Celui-ci permet de gérer plusieurs implémentations d'une même classe au sein d'une archive unique. Avant Java 9, il était donc impossible de gérer plusieurs versions d'une même bibliothèque au sein d'une même archive.
Il était important d'apporter une solution pour assurer la compatibilité descendante, car Java 9 intègre de nouvelles API permettant de coder différemment . À l'exécution, la bonne version de la classe sera chargée automatiquement en fonction de la version de Java utilisé. Cette solution est décrite dans la JEP 238, elle est appelée Multi-Release JAR (MR JAR pour les intimes).
Si on considère deux classes Foo et Bar. La seconde contient deux implémentations une pour Java 9 et une autre pour toutes les versions antérieures à Java 9. La structure du JAR sera la suivante :
2.
3.
4.
5.
6.
7.
8.
JAR root
- Foo.class
- Bar.class
+ META-INF
- MANIFEST.MF
+ versions
+ 9
- Bar.class
Comme on peut constater la racine (JAR root) n'évolue pas et Foo.class et Bar.class seront utilisées par toutes les versions de Java ne supportant pas MR JAR. Au contraire Foo.class et Bar.class localisée dans META-INF/versions/9/Bar.class seront utilisées pour la version 9 de Java.
Cette fonctionnalité a demandé des améliorations au niveau des outils de compilation et d'analyse. Les outils du JDK (javac, javap, jdeps...) ont été bien entendu impactés. Les outils de build comme Maven ou Gradle ont dû s'adapter pour la construction de cette nouvelle structure de JAR. Sur le papier tout fonctionne pour preuve dans ce dépôt GIT où des tests ont été réalisés.
V. Un shell Java : REPL jShell▲
De nombreux langages sont actuellement très populaires grâce à leur simplicité pour l'apprentissage ; à titre d'exemple le langage Python. Cette simplicité est en partie due à la présence d'une implémentation appelée REPL (Read Evaluate Print Loop). Grâce à ce mode de fonctionnement basé sur une boucle, l'interpréteur :
- Lit une expression (le R pour Read) ;
- Évalue une expression (le E pour Evaluate) ;
- Imprime sur la sortie standard (le P pour Print) ;
- Recommence (le L pour Loop).
Pour pallier ce manque, la version Java 9 offre un REPL appelé JShell ( JEP 222) permettant une programmation interactive. JShell interprète directement des expressions sans avoir besoin qu'elles soient enveloppées dans une classe ou une méthode.
Outre l'aspect apprentissage, JShell pourra être utilisé pour tester du code rapidement en quelques lignes de code. Par exemple, vérifier si un service web est présent ou tester des agents de placement pour JavaFX. Il est également prévu de pouvoir intégrer JShell directement dans du code Java. On peut même envisager une alternative aux bons vieux scripts Bash et pourquoi pas des langages de script de la JVM comme Groovy.
Nous donnons ci-dessous à titre d'exemple les principales actions pouvant être réalisées avec JShell.
V-A. Exécution▲
Démarrer JShell en appelant la commande jshell. L'option -v permet d'exécuter JShell avec le mode verbeux (verbose).
2.
3.
4.
5.
6.
7.
$ jshell -v
Jul 19, 2017 4:32:56 PM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
| Welcome to JShell -- Version 9-Debian
| For an introduction type: /help intro
jshell >
V-B. Expressions▲
Vous pouvez saisir n'importe quelle expression tant qu'elle est valide du point de vue Java. Dans ce cas, le résultat de l'expression est stocké dans une variable déclarée automatiquement par JShell.
2.
3.
4.
5.
6.
7.
jshell > 40 + 2
$1 ==> 42
| created scratch variable $1 : int
jshell > $1
$1 ==> 42
| value of $1 : int
V-C. Variables▲
Si vous souhaitez déclarer une variable et la nommer.
2.
3.
jshell > int myExp = 40 + 2
myexp ==> 42
| created variable myexp : int
V-D. Méthodes▲
Pour définir des méthodes, il suffit de saisir la signature de la méthode et le corps. Attention même si JShell n'est pas regardant sur l'utilisation des “;”, prenez soin de placer des “;” quand vous êtes dans un bloc.
2.
3.
4.
5.
jshell> void helloDeveloppezCom() {
...> System.out.println("Hello Developpez.com, Java 9 is out!!!");
...> }
| created method helloDeveloppezCom()
…>
Pour l'appel, il ne reste qu'à nommer la méthode. Pensez à utiliser l'auto-complétion.
2.
jshell> helloDeveloppezCom()
Hello Developpez.com, Java 9 is out!!!
V-E. Commandes▲
Différentes commandes sont disponibles et la liste est accessible en affichant l'aide via /help. Les commandes les plus utiles sont :
/vars pour l'affichage des variables
2.
3.
jshell> /vars
| int $1 = 42
| int myExp = 42
/methods pour l'affichage des méthodes
2.
3.
jshell> /methods
| printf (String,Object...)void
| helloDeveloppezCom ()void
/list pour afficher l'ensemble des instructions déjà saisies
2.
3.
4.
5.
6.
7.
8.
jshell> /list
1 : 40 + 2
2 : int myExp = 40 + 2;
3 : void helloDeveloppezCom() {
System.out.println("Hello Developpez.com, Java 9 is out!!!");
}
4 : helloDeveloppezCom()
Il y a des petites choses à améliorer comme par exemple l'obligation de déclarer les variables ou l'absence de binding qui permet de réévaluer les expressions (par exemple z = x+ y, si x ou y change, z conservera toujours la même valeur).