Java : vue d'ensemble¶
Avant d'écrire du code, une carte du territoire : ce qu'est vraiment « Java », comment ton code s'exécute, et en quoi il diffère de Rust, C++ et Python.
Dans ce chapitre
- Java = un langage + une machine virtuelle (la JVM) + une grosse bibliothèque standard
- Le trajet
.java→ bytecode → code machine, et le rôle du JIT et du GC - Java face à Rust, C++ et Python, point par point
- Ce qui va te surprendre en arrivant
- Les conventions de nommage, et le plan de la partie Java
Java, c'est trois choses¶
Quand on dit « Java », on parle en fait de trois éléments distincts :
- Le langage
- La syntaxe et les règles de typage (classes, interfaces, génériques,
record...). Le compilateurjavacle traduit en bytecode, pas en code machine. - La JVM (Java Virtual Machine)
- Le programme qui exécute le bytecode : elle charge les classes, les vérifie, les interprète, compile à la volée les parties chaudes et gère la mémoire. D'autres langages ciblent la même JVM (Kotlin, Scala, Clojure).
- La bibliothèque standard
- Le JDK livre des milliers de classes : collections, E/S, réseau, HTTP, dates, concurrence, cryptographie... C'est beaucoup plus large que la
stdde Rust, plus proche des « piles incluses » de Python.
Le JDK (Java Development Kit) regroupe les trois, plus les outils (javac, jshell, javap, jar...). Tu l'installes au chapitre suivant.
Du fichier source à l'exécution¶
flowchart LR
A["Calcul.java<br/>(source)"] -->|javac| B["Calcul.class<br/>(bytecode portable)"]
subgraph JVM["JVM"]
direction TB
C["Chargeur de classes<br/>+ vérificateur"] --> D["Interpréteur"]
D -->|"méthodes chaudes"| E["JIT : compilation<br/>en code machine"]
F["GC : ramasse-miettes"]
end
B -->|java Calcul| JVM
Prenons un programme minuscule :
public class Calcul {
static int carre(int x) {
return x * x;
}
public static void main(String[] args) {
System.out.println(carre(7)); // affiche : 49
}
}
javac Calcul.java produit Calcul.class. L'outil javap -c montre le bytecode de carre (commentaires ajoutés) : des instructions pour une machine à pile, indépendantes du processeur.
static int carre(int);
Code:
0: iload_0 // empile le 1er argument
1: iload_0 // l'empile encore
2: imul // multiplie les deux sommets de pile
3: ireturn // renvoie le résultat
Ce même .class tourne sur Linux, macOS ou Windows, x86 ou ARM : c'est le fameux write once, run anywhere. Pas besoin de recompiler par cible comme en Rust ou en C++.
Sous le capot : interprète, JIT et GC
- Au démarrage, la JVM interprète le bytecode (lent mais immédiat).
- Elle compte les appels : une méthode appelée souvent est compilée en code machine par le JIT (Just-In-Time), d'abord rapidement (compilateur C1), puis de façon très optimisée (C2) en s'appuyant sur ce qu'elle a observé à l'exécution (types réels, branches prises). Elle peut même revenir en arrière si une hypothèse devient fausse.
- Le GC (Garbage Collector, ramasse-miettes) libère les objets inaccessibles. Par défaut c'est G1 ; d'autres existent (ZGC vise des pauses de moins d'une milliseconde).
Conséquence pratique : un programme Java est lent pendant ses premières secondes (phase de warm-up), puis atteint des performances proches du natif. C'est idéal pour un serveur qui tourne des jours, moins pour un petit outil en ligne de commande.
Un fichier .class commence toujours par les octets CA FE BA BE, suivis du numéro de version du format : 69 pour Java 25. Une JVM plus ancienne refuse un .class trop récent.
Java face à ce que tu connais¶
| Aspect | Rust | Java |
|---|---|---|
| Compilation | native, en avance (LLVM) | bytecode, puis JIT dans la JVM |
| Mémoire | possession et emprunts, libération déterministe | GC, libération quand il le décide |
| Typage | statique, inférence très large | statique, inférence locale (var) |
| Absence de valeur | Option<T> |
null partout, Optional<T> en retour |
| Erreurs | Result<T, E>, panic! |
exceptions, certaines vérifiées par le compilateur |
| Génériques | monomorphisés | effacés à la compilation (type erasure) |
| Performance | maximale, démarrage instantané | proche après chauffe, démarrage plus lent, plus de RAM |
| Aspect | C++ | Java |
|---|---|---|
| Compilation | native, une par plateforme | bytecode portable + JIT |
| Mémoire | manuelle / RAII, objets sur la pile par défaut | objets toujours sur le tas, GC |
| Méthodes | non virtuelles par défaut | toutes virtuelles |
| Héritage | multiple | simple, plus des interfaces multiples |
| Absence de valeur | nullptr, comportement indéfini si déréférencé |
null, NullPointerException (jamais d'UB) |
| Erreurs | exceptions ou codes de retour | exceptions partout |
| Performance | maximale, contrôle total | proche après chauffe, moins de contrôle |
| Aspect | Python | Java |
|---|---|---|
| Exécution | bytecode .pyc interprété |
bytecode .class interprété puis compilé (JIT) |
| Typage | dynamique, annotations facultatives | statique, vérifié à la compilation |
| Mémoire | comptage de références + GC des cycles | GC traçant, pas de compteur |
| Absence de valeur | None |
null |
| Erreurs | exceptions | exceptions, certaines vérifiées par le compilateur |
| Parallélisme | limité par le GIL (par défaut) | vrais threads parallèles |
| Performance | lent sur du calcul pur | nettement plus rapide après chauffe |
En résumé : Java a le typage statique de Rust et C++, mais le modèle mémoire de Python (tout objet est manipulé par référence et libéré automatiquement).
Ce qui va te surprendre¶
- Tout est classe
- Pas de fonction libre : tout code vit dans une classe (sauf dans un petit fichier « compact » de Java 25, vu au chapitre suivant). Seuls 8 types primitifs (
int,double,boolean...) ne sont pas des objets. Voir Syntaxe et types. - Des références partout
- Une variable de type objet contient une référence, jamais l'objet lui-même.
a = bne copie rien : les deux noms désignent le même objet. Voir Mémoire et références. - Le GC
- Pas de
free, pas dedelete, pas de destructeur appelé à un moment précis. Pour fermer un fichier, on utilisetry-with-resources, l'équivalent dewithen Python. - Les exceptions
- C'est le mécanisme d'erreur standard, et le compilateur t'oblige à traiter certaines d'entre elles (les checked exceptions). Voir Exceptions et Optional.
null- N'importe quelle référence peut valoir
null, et le compilateur ne t'aide pas. LaNullPointerExceptionsera ta compagne de route. - Java moderne est concis
- La réputation de verbosité vient du Java des années 2000. Depuis Java 8 (lambdas), 10 (
var), 16 (record) et 21 (pattern matching), le code ressemble beaucoup plus à du Kotlin ou du Rust.
Compare le même programme écrit « à l'ancienne » et en Java 25 :
static final class Personne {
private final String nom;
private final int age;
Personne(String nom, int age) {
this.nom = nom;
this.age = age;
}
public String getNom() { return nom; }
public int getAge() { return age; }
// + equals(), hashCode() et toString() écrits à la main (une quinzaine de lignes)
}
// dans main :
List<String> plusDe30 = new ArrayList<String>();
for (Personne p : gens) {
if (p.getAge() >= 30) {
plusDe30.add(p.getNom());
}
}
record Personne(String nom, int age) {} // equals, hashCode, toString inclus
void main() {
var gens = List.of(new Personne("Ada", 36), new Personne("Linus", 28));
var plusDe30 = gens.stream()
.filter(p -> p.age() >= 30)
.map(Personne::nom)
.toList();
IO.println(plusDe30); // affiche : [Ada]
}
Tu croiseras les deux styles : beaucoup de code existant (et de réponses sur Internet) date d'avant Java 17. Ce cours utilise le style moderne, et signale l'ancien quand il est encore courant.
Conventions de nommage¶
Java est très homogène sur ce point, et Spring s'appuie parfois sur ces noms (par exemple les getters, voir Classes et objets).
| Élément | Convention | Exemple |
|---|---|---|
| Classe, interface, record, enum | PascalCase |
TodoService, HttpClient |
| Méthode, variable, paramètre | camelCase |
findById, nbTaches |
Constante (static final) |
MAJUSCULES_SOULIGNEES |
MAX_TENTATIVES |
| Constante d'enum | MAJUSCULES |
Statut.EN_COURS |
| Paquet | minuscules, domaine inversé | xyz.dunkan.todo.web |
| Paramètre de type générique | une majuscule | T, E, K, V |
Piège : pas de snake_case
Contrairement à Rust et Python, les méthodes et variables sont en camelCase : find_by_id choque tout lecteur Java. Et dans Spring Data, le nom d'une méthode est même analysé pour générer une requête (findByTitreContaining) : la casse compte.
Le domaine inversé sert à garantir des noms de paquets uniques au monde : si tu possèdes dunkan.xyz, tes paquets commencent par xyz.dunkan. Le paquet correspond à un dossier (xyz/dunkan/todo/), comme un module Rust ou un paquet Python.
Plan de la partie Java¶
| Chapitre | Ce que tu y apprends |
|---|---|
| Installer et lancer | JDK 25, java Fichier.java, javac, jshell, IDE |
| Syntaxe et types | primitives, String, switch moderne, tableaux, méthodes |
| Mémoire et références | pile et tas, passage par valeur, == vs equals, null, GC, ressources |
| Classes et objets | classes, visibilité, héritage, interfaces, equals/hashCode |
| Records, enums, sealed | types algébriques à la Rust, pattern matching |
| Génériques et collections | List, Map, Set, génériques et leurs limites |
| Exceptions et Optional | try/catch, exceptions vérifiées, Optional |
| Lambdas et streams | fonctions en paramètre, traitements de collections façon itérateurs Rust |
| Threads et concurrence | threads, executors, threads virtuels, synchronisation |
| Annotations et réflexion | les mécanismes qui font fonctionner Spring |
| Maven | pom.xml, dépendances, cycle de build, le wrapper |
Comment travailler
Chaque chapitre se termine par des exercices corrigés. Tape le code toi-même : java Fichier.java suffit pour tout exécuter jusqu'au chapitre Maven, sans projet ni configuration.