|
|
|
La suite aborde le langage Java sous l'angle de la compilation et de l'exécution des programmes.
|
|
|
|
|
|
|
|
**Table des matières**
|
|
|
|
|
|
|
|
[[_TOC_]]
|
|
|
|
|
|
|
|
# Java en bref, éditions / distributions
|
|
|
|
|
|
|
|
**Java** est un langage de programmation dont la syntaxe possède de nombreuses similarités avec à celle du langage C (voir plus bas l'exemple `HelloWorld`). C'est néanmoins un langage orienté objet, en ce sens il est plus proche de C++.
|
|
|
|
|
|
|
|
Java est un langage **compilé**, il faut donc un **compilateur Java** pour passer du code source (`.java`) au code binaire (`.class`). Le code binaire n'est pas directement exécutable sur une machine hôte, il s'agit de **bytecode** destiné à être exécuté sur une **machine virtuelle.**
|
|
|
|
|
|
|
|
Pour exécuter une application Java, il faut disposer d'une **machine virtuelle Java**, appelée communément **JVM** (_Java Virtual Machine_).
|
|
|
|
|
|
|
|
> :rocket: Pour en savoir plus, voir `Compilation et exécution : Java vs C` dans la section `Pour aller plus loin`
|
|
|
|
|
|
|
|
Les applications Java reposent sur une **bibliothèque standard** qui, comme la `libc` pour les applications C, fournit le support pour la manipulation de chaines de caractères, les fonctions mathématiques, mais aussi la communication réseau, l'accès aux bases de données, ...
|
|
|
|
|
|
|
|
Il existe plusieurs **éditions** (appelées aussi plate-formes) qui se distinguent par la bibliothèque standard fournie (et quelque fois la machine virtuelle), dont les 2 principales sont :
|
|
|
|
|
|
|
|
* **Java SE** (Standard Edition), qui permet de réaliser des applications en mode terminal ou graphiques, destinées à être installées et exécutées sur un poste de travail
|
|
|
|
* **Jakarta EE** (Enterprise Edition), qui permet de réaliser des applications Web
|
|
|
|
|
|
|
|
> :warning: L'édition qui fait l'objet de ce guide est Java SE.
|
|
|
|
|
|
|
|
On distingue dans Java SE :
|
|
|
|
|
|
|
|
* le **JRE** (_Java Runtime Environment_) qui ne contient que la machine virtuelle et la bibliothèque standard
|
|
|
|
* le **JDK** (_Java Development Kit_), qui contient le JRE et les outils de développement dont notamment le compilateur Java
|
|
|
|
|
|
|
|
> :rocket: Pour en savoir plus, voir `Modèle de licence Java SE, versions LTS / non-LTS, distributions` dans la section `Pour aller plus loin`
|
|
|
|
|
|
|
|
# Installation du JDK
|
|
|
|
|
|
|
|
Il existe une variété de **distributions** fournies par des éditeurs de logiciels ou des communautés de développeurs, parmis lesquelles :
|
|
|
|
|
|
|
|
* [Oracle JDK](https://www.oracle.com/java/technologies/downloads/#java17 "Oracle JDK") / OpenJDK
|
|
|
|
* [Adoptium Eclipse Temurin](https://adoptium.net/en-GB/ "Adoptium Eclipse Temurin JDK")
|
|
|
|
* (en savoir plus...)
|
|
|
|
|
|
|
|
> :warning: Dans la suite, on considérera que la distribution choisie est **Adoptium Eclipse Temurin 17.0.5**
|
|
|
|
|
|
|
|
L'installation du JDK sur un poste de travail est en règle générale assez simple et automatique, mais diffère selon le système d'exploitation et la distribution choisie. Il faut donc se référer à la documentation liée à la distribution choisie.
|
|
|
|
|
|
|
|
Une fois la distribution installée, les outils (compilateur, machine virtuelle) et la bibliothèque standard sont disponibles. Pour vérifier que la distribution est bien installée, il suffit d'ouvrir un terminal et d'exécuter la machine virtuelle Java (`java`) en lui demandant d'afficher la version courante :
|
|
|
|
|
|
|
|
```bash
|
|
|
|
sebastienjean@Air-SebJ ~ % java -version
|
|
|
|
openjdk version "17.0.5" 2022-10-18
|
|
|
|
OpenJDK Runtime Environment Temurin-17.0.5+8 (build 17.0.5+8)
|
|
|
|
OpenJDK 64-Bit Server VM Temurin-17.0.5+8 (build 17.0.5+8, mixed mode)
|
|
|
|
sebastienjean@Air-SebJ ~ %
|
|
|
|
```
|
|
|
|
|
|
|
|
# Compilation et exécution d'une application Java
|
|
|
|
|
|
|
|
Les applications Java sont structurés en **classes** (les classes feront l'objet d'un autre document).
|
|
|
|
|
|
|
|
Les classes Java ont un **nom**, qui commence par convention par une majuscule, et doivent être écrites dans des fichiers (source) qui portent l'extension `.java`.
|
|
|
|
|
|
|
|
Un fichier source Java contient la définition d'une classe (via le mot-clé `class`) :
|
|
|
|
|
|
|
|
```
|
|
|
|
public class NomDeLaClasse {
|
|
|
|
(contenu de la classe)
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
La classe `HelloWorld`, qui affiche un message sur la sortie standard (console), sera donc écrite dans le fichier `HelloWorld.java`, situé dans le répertoire courant :
|
|
|
|
|
|
|
|
```bash
|
|
|
|
sebastienjean@Air-SebJ % ls
|
|
|
|
HelloWorld.java
|
|
|
|
sebastienjean@Air-SebJ %
|
|
|
|
```
|
|
|
|
|
|
|
|
Le contenu du fichier `HelloWorld.java`, pour produire le résultat attendu, est le suivant :
|
|
|
|
|
|
|
|
```java
|
|
|
|
public class HelloWorld {
|
|
|
|
public static void main(String[] args) {
|
|
|
|
System.out.println("Hello, World");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
La classe `HelloWorld` est le point d'entrée d'une *application* Java car cette classe définit la *méthode* `main`.
|
|
|
|
|
|
|
|
> :pencil: La signature de la méthode `main` de Java est similaire à celle de C mais elle est **unique et doit être rigoureusement respectée**. Le paramètre `args` permet de fournir des arguments à l'application lors de son exécution (évoqué plus loin).
|
|
|
|
|
|
|
|
Le terme _méthode_ est utilisé spécifiquement en programmation orientée objet, sa signification est proche de celle de _fonction_.
|
|
|
|
|
|
|
|
La **compilation** de cette classe s'effectue en invoquant le compilateur Java (`javac`) :
|
|
|
|
|
|
|
|
```bash
|
|
|
|
sebastienjean@Air-SebJ % javac HelloWorld.java
|
|
|
|
sebastienjean@Air-SebJ %
|
|
|
|
```
|
|
|
|
|
|
|
|
Ici, la compilation s'effectue sans erreur (pas de message particulier), et le contenu du répertoire fait apparaitre l'existence du fichier binaire (`.class`) de la classe :
|
|
|
|
|
|
|
|
```bash
|
|
|
|
sebastienjean@Air-SebJ % ls
|
|
|
|
HelloWorld.class HelloWorld.java
|
|
|
|
sebastienjean@Air-SebJ %
|
|
|
|
```
|
|
|
|
|
|
|
|
> :rocket: Pour en savoir plus sur le bytecode, voir `Bytecode Java` dans la section `Pour aller plus loin`
|
|
|
|
|
|
|
|
L'**exécution** de l'application s'effectue en invoquant la machine virtuelle Java et en lui passant le nom de la classe à exécuter :
|
|
|
|
|
|
|
|
```bash
|
|
|
|
sebastienjean@Air-SebJ % java HelloWorld
|
|
|
|
Hello, World
|
|
|
|
sebastienjean@Air-SebJ %
|
|
|
|
```
|
|
|
|
|
|
|
|
Si l'on veut prendre le message à afficher en argument de la ligne de commande (sous la forme d'une chaîne de caractères), il suffit de modifier le code source de la classe de la manière suivante :
|
|
|
|
|
|
|
|
```
|
|
|
|
public class HelloWorld {
|
|
|
|
public static void main(String[] args) {
|
|
|
|
System.out.println(args[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Une fois recompilée, l'application doit être exécutée de la manière suivante pour produire le même résultat :
|
|
|
|
|
|
|
|
```bash
|
|
|
|
sebastienjean@Air-SebJ helloworld % java HelloWorld "Hello, World"
|
|
|
|
Hello, World
|
|
|
|
sebastienjean@Air-SebJ helloworld %
|
|
|
|
```
|
|
|
|
|
|
|
|
Les arguments de la ligne de commande sont des chaines de caractères séparées par un espace, le premier argument correspond à l'indice `0` du tableau `args`. Si une des chaînes de caractères passées en argument comporte des espaces, il faut la protéger avec des `"`.
|
|
|
|
|
|
|
|
* `java HelloWorld "Hello, World" another` exécute l'application en lui passant 2 arguments (`Hello, World` et `another`)
|
|
|
|
* `java HelloWorld Hello, World another` exécute l'application en lui passant 3 arguments (`Hello,`<sub> ,</sub> `World` et `another`)
|
|
|
|
|
|
|
|
# Pour aller plus loin ...
|
|
|
|
|
|
|
|
La suite contient des compléments, non indispensables immédiatement, pour les curieux avides de détails.
|
|
|
|
|
|
|
|
## Compilation et exécution : Java _vs_ C
|
|
|
|
|
|
|
|
### Programme écrit en langage C
|
|
|
|
|
|
|
|
Dans un langage procédural et compilé tel que C, le cycle de production de l'exécutable à partir des sources suit un ensemble d'étapes présentées de manière simplifiée sur le schéma.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
Le langage C est standardisé, sa libraire standard aussi, et un programme n'allant pas au delà de cette utilisation standard n'a pas besoin d'être réécrit en soi pour s'exécuter sur des couples matériel/système différents.
|
|
|
|
|
|
|
|
1. Le **code source** d'un programme C passe d'abord dans un outil appelé **préprocesseur** qui va traiter les inclusions (`#include`), remplacer/appliquer les _alias_ / _macros_ (#define, facilitant grandement l'écriture des programmes par le développeur). Il en ressort un autre code source dans lequel tout est complètement visible et reste éditable.
|
|
|
|
|
|
|
|
2. Ce fichier source passe ensuite dans un **compilateur C**. Le compilateur est **spécifique à un couple matériel/système** donné. Ici, c'est un compilateur destiné à produire du code exécutable pour un système _Linux_ tournant sur une architecture _Intel x86_. Le compilateur vérifie que la syntaxe du programme est correcte et traduit le code source en instructions du langage natif cible, ici le langage d'assemblage _x86_, tout en respectant des conventions et règles propres au système d'exploitation _Linux_. Il en résulte un fichier encore modifiable ou lisible par le développeur, contenant le **code assembleur**.
|
|
|
|
|
|
|
|
> Le compilateur peut générer du code pour une cible matérielle/système différente de celle sur laquelle il s'exécute lui-même, on parle alors de **compilation croisée** (_cross-compilation_), technique utilisée très fréquemment dans le domaine de l'embarqué.
|
|
|
|
|
|
|
|
3. Le code assembleur passe ensuite dans un outil appelé **assembleur**, qui produit un **code binaire** appelé **fichier objet** pas encore tout à fait exécutable en général. Il reste à établir les connexions avec les éventuels autres fichiers objets participant à l'application et compilés indépendamment au préalable. Cette étape est réalisée par l'**éditeur de liens** duquel ressort un **fichier binaire unique** et exécutabl
|
|
|
|
4. L'exécution de ce binaire se déroulera normalement sur le couple matériel/système prévu (ici _x86-Linux_) mais échouera sur tout autre couple différent.
|
|
|
|
|
|
|
|
> Il faudra donc compiler le même code pour chaque couple matériel/système sur lequel on souhaite qu'il puisse s'exécuter, en utilisant la chaine de compilation adéquate composée de l'ensemble des outils évoqués précédemment.
|
|
|
|
|
|
|
|
> L'avantage des langages compilés classiques tels que C est qu'ils produisent un exécutable au plus près du matériel et du système, sans rajouter de surcouche inutile. Ils sont donc extrêmement compacts (et leur taille indique quasiment la place que leur code va réellement occuper en mémoire) et extrêmement performants.
|
|
|
|
|
|
|
|
### Programme écrit en langage Java
|
|
|
|
|
|
|
|
Java opte pour une vision intermédiaire, illustrée ci-dessous, en étant compilé vers un langage intermédiaire interprété ou recompilé à la volée sur chaque cible matériel/système.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
1. Le code source Java (`.java`) est d'abord compilé par un **compilateur Java** (universel) vers un langage natif fictif, celui de la machine virtuelle Java.
|
|
|
|
|
|
|
|
> Une machine virtuelle possède un langage natif proche du langage natif d'une machine réelle mais indépendant d'une architecture particulière. Lorsqu'une machine virtuelle exécute un programme, elle **traduit** chaque instruction vers une (ou plusieurs) instruction(s) du langage natif de la machine réelle (appelée aussi hôte). Cette traduction peut se faire :
|
|
|
|
* à la volée (les instructions sont interprétées les unes à la suite des autres)
|
|
|
|
* en une seule fois (le programme exprimé dans le langage de la machine virtuelle est entièrement compilé vers le langage natif de la machine hôte puis ce code natif est exécuté par la machine hôte elle-même)
|
|
|
|
|
|
|
|
2. Sur chaque cible matériel/système, ce fichier binaire (`.class`) sera exécuté par une JVM adaptée. Les librairies accompagnant la machine virtuelle sont elles aussi spécifiques à la cible puisqu'elles contiennent en général elles-même du code natif dépendant de cette cible.
|
|
|
|
|
|
|
|
> Ainsi, en écrivant le code source une seule fois et en produisant le binaire une seule fois, ce binaire pourra être exécuté sans autre intervention sur toutes les cibles matériel/système disposant d'une JVM, avec des performances relativement équivalentes (la différence n'est pas très significative, en réalité c'est surtout le coût de démarrage de l'application qui est pénalisant. Ce coût devient négligeable quand l'exécution de l'application se fait sur un temps long).
|
|
|
|
|
|
|
|
## Bytecode Java
|
|
|
|
|
|
|
|
Un fichier `.class`n'est pas lisible tel quel, c'est un format binaire et non texte, mais il est possible avec l'outil `javap` de décompiler / désassembler le bytecode pour avoir une idée de ce à quoi ressemble le langage natif de la machine virtuelle Java :
|
|
|
|
|
|
|
|
```bash
|
|
|
|
sebastienjean@Air-SebJ % javap -c HelloWorld
|
|
|
|
Compiled from "HelloWorld.java"
|
|
|
|
public class HelloWorld {
|
|
|
|
public HelloWorld();
|
|
|
|
Code:
|
|
|
|
0: aload_0
|
|
|
|
1: invokespecial #1 // Method java/lang/Object."<init>":()V
|
|
|
|
4: return
|
|
|
|
|
|
|
|
public static void main(java.lang.String[]);
|
|
|
|
Code:
|
|
|
|
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
|
|
|
|
3: ldc #13 // String Hello, World
|
|
|
|
5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
|
|
|
|
8: return
|
|
|
|
}
|
|
|
|
sebastienjean@Air-SebJ %
|
|
|
|
```
|
|
|
|
|
|
|
|
On voit qu'il est proche d'un langage d'assemblage, certaines instructions ressemblent à des chargements de valeurs dans des registres, à des sauts, ...
|
|
|
|
|
|
|
|
## Modèle de licence Java SE, versions LTS / non-LTS, distributions
|
|
|
|
|
|
|
|
Même si il est la propriété d'Oracle, **Java SE est utilisable gratuitement sans conditions**.
|
|
|
|
|
|
|
|
Il évolue très régulièrement (une version tous les 6 mois dorénavant) par l'ajout d'éléments de langage, de bibliothèques et d'outils.
|
|
|
|
|
|
|
|
Certaines versions dites **LTS** (_Long Term Support_) disposent d'un support à long terme (correction de vulnérabilités, optimisation, ...). Les dernières versions sont la version 17 (LTS, sept 2021) et 19 (non LTS, sept 2022). La prochaine version LTS, 21, est prévue pour septembre 2023.
|
|
|
|
|
|
|
|
Les distributions Java correspondent à des versions précompilées et _packagées_ des outils de développement Java (compilateur, machine virtuelle, ...)
|
|
|
|
|
|
|
|
Techniquement il n'existe pas de différence notable entre les distributions Java mais certaines disposent d'outils supplémentaires, ont des performances plus élevées sur certains aspects, ... Certaines sont libres (leur code est ouvert), d'autres non (par exemple Oracle JDK n'est pas libre, Oracle OpenJDK oui).
|
|
|
|
|
|
|
|
## Popularité et usages de Java
|
|
|
|
|
|
|
|
L'[index Tiobe](https://www.tiobe.com/tiobe-index/) permet de mesurer la part des langages dans les développements. Java figure depuis 20 ans parmi les 3 ou 4 langages majeurs qui représentent à eux seuls plus de 50% des développements.
|
|
|
|
|
|
|
|
Cependant, selon [W3Techs](https://w3techs.com/technologies/details/pl-java), l'usage de Java pour les applications Web reste faible en volume (5%, en fait 80% des applications Web sont écrites en PhP).
|
|
|
|
|
|
|
|
Java est donc plutôt destiné à des applications sur poste de travail (dites aussi clients lourds lorsqu'il s'agit d'applications client-serveur), ou des applications mobile (_Android_).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|