Java : d’où ça vient, pourquoi c’est comme ça

Java
Lecture
Origines et filiation du langage Java
Auteur
Affiliations

Université de Toulon

LIS UMR CNRS 7020

Date de publication

2026-11-02

Bien des aspects de Java surprennent même après une première vue d’ensemble : l’absence de surcharge d’opérateurs, l’héritage multiple interdit, les exceptions que le compilateur vous impose de traiter, ou encore ce réflexe de tout faire passer par une machine virtuelle. Cette leçon soutient une thèse simple : rien de tout cela n’est une excentricité. Chaque particularité est la trace d’un arbitrage posé au début des années 1990, dans des contraintes précises. Comprendre ces arbitrages, c’est déjà apprendre à lire le langage. Le vocabulaire objet — classe, objet, attribut, message, encapsulation — ainsi que la spécialisation et le polymorphisme, UML en bref et un panorama de langages, et une première vue d’ensemble de Java (ses caractéristiques, l’outillage minimal) ont été posés dans la première séance, « Le paradigme objet » (00_java_L_paradigme_objet.qmd) ; cette seconde séance explique pourquoi Java ressemble à ce qu’il est.

1991 : le projet Green et l’électronique embarquée

Java naît chez Sun Microsystems, sous l’impulsion de James Gosling et de son équipe. Les origines du langage remontent à 1991 et au projet « Green », dont l’objectif était de développer une technologie pour les appareils électroménagers intelligents. L’équipe constate très vite que les langages existants — C et C++ — sont inadaptés à cette cible : les appareils visés ont des capacités matérielles hétérogènes et limitées, sont destinés à être connectés au réseau, et doivent fonctionner des années sans maintenance logicielle. Un langage dont la fiabilité dépend de la rigueur manuelle du programmeur dans la gestion de la mémoire n’y suffit pas.

De cette origine embarquée vient la matrice de toutes les décisions que nous allons examiner : Java n’a pas été conçu pour un système que le programmeur contrôle, mais pour une ferme de machines que personne ne contrôle entièrement. La portabilité, la robustesse et la sécurité ne sont pas des fonctionnalités ajoutées après coup : ce sont les exigences fondatrices. Le langage est officiellement lancé en 1995, et c’est sa capacité à fonctionner sur n’importe quel dispositif grâce à la machine virtuelle qui en fera, dans les années qui suivent, un outil central du développement web et des applications distribuées.

Concevoir contre C++ : des arbitrages, pas des excentricités

L’équipe de Green a pratiqué C++ et vu ses fragilités sur le terrain. Le langage qui deviendra Java est délibérément conçu contre C++, avec un objectif affiché : simplifier le développement en éliminant les aspects complexes et sources d’erreurs. Quatre choix méritent d’être présentés non comme des interdits arbitraires, mais comme des échanges — chaque renoncement achète une garantie, et chaque garantie a un prix.

Premier arbitrage : la surcharge d’opérateurs, abandonnée. En C++, on peut redéfinir + ou == pour ses propres types ; le même symbole peut alors signifier des choses très différentes selon le contexte, et un code formellement correct devient sémantiquement imprévisible. Java renonce à ce pouvoir : une opération s’exprime par une méthode nommée, point. Le prix payé est l’absence de notation infixée pour les types utilisateur ; ce qui est gagné, c’est qu’un appel se lit toujours de la même façon. Nous verrons, en manipulant les collections, à quoi ressemble ce style « tout est méthode » dans la pratique.

Deuxième arbitrage : l’héritage multiple de classes, refusé. Quand une classe hérite de deux ancêtres qui se recouvrent, la résolution des conflits — le problème classique du diamant — est une source durable de bogues subtils. Java tranche : un seul héritage de classe, mais la possibilité de réaliser plusieurs interfaces — c’est-à-dire d’appartenir à plusieurs types sans hériter deux fois d’une implémentation. Ce choix réoriente tout le style de conception Java : plutôt que de composer des hiérarchies complexes, on assemble des objets. La maxime « préférer la composition à l’héritage » n’est pas une mode : cet arbitrage est l’un des points de départ de ce principe, que les séances de réutilisation développeront.

Troisième arbitrage : la mémoire gérée par le runtime. En C++, le programmeur alloue et libère ; l’oublier produit une fuite, le faire deux fois ou trop tôt produit un pointeur pendillant (dangling pointer) : accès à une mémoire déjà libérée — famille d’erreurs structurellement ingérables à l’échelle d’un système réparti. Java confie la libération de la mémoire à un ramasse-miettes (garbage collector) intégré à l’exécution. Le prix payé : l’exécution doit connaître le graphe des objets, les libérations ne sont pas prévisibles à la ligne près, et certaines pauses accompagnent le nettoyage. Ce qui est gagné : la disparition pure et simple de toute une catégorie de bogues — les erreurs de libération. Vous le vivrez dès les premières heures de code, sans jamais le remarquer — c’est le signe que l’arbitrage fonctionne.

Quatrième arbitrage : les exceptions checkées. Java distingue les erreurs qu’un appelant peut raisonnablement traiter (elles doivent être déclarées dans la signature de la méthode et soit capturées, soit propagées) des erreurs de programmation, laissées libres. C’est le choix le plus discuté du langage : sa verbosité est régulièrement critiquée, et beaucoup de frameworks modernes s’en accommodent autrement. Mais la logique est celle du projet Green : dans un système où des sous-programmes écrits par des inconnus s’appellent à distance, « cette méthode peut échouer ainsi » fait partie du contrat, et le compilateur refuse qu’on ignore le contrat. Nous y reviendrons longuement au moment de connecter une base de données, là où ce choix devient tangible.

1995 et le pari de la JVM

La réponse de Java à l’hétérogénéité des machines ne consiste pas à compiler pour chaque plateforme — ce qui multiplierait les chaînes d’outils et les versions à maintenir. Elle consiste en un pari d’architecture : insérer une représentation intermédiaire et une machine abstraite. Le compilateur javac traduit les fichiers .java en bytecode (fichiers .class), un pseudo-code défini par une spécification, compréhensible par la machine virtuelle Java (JVM). C’est la JVM qui est réimplémentée pour chaque système (Windows, Unix, macOS), une seule fois chacun ; le bytecode, lui, est produit une seule fois pour tous. D’où la formule consacrée : « compiler une fois, exécuter partout ».

Ce pari a un prix : le programme n’est pas exécuté directement par le processeur. La JVM interprète le bytecode, et rattrape la performance par une compilation à chaud (just-in-time, JIT) qui traduit les portions de code chaud en instructions natives pendant l’exécution ; des compilations anticipées (AOT) existent également. Le gain, lui, dépasse la portabilité : la JVM est un point de contrôle unique qui vérifie le bytecode à son chargement, d’où une exécution contrôlée, et offre un sol commun à d’autres langages. Surtout, ce pari explique la forme concrète des premiers travaux pratiques du cours : lorsque vous compilerez puis exécuterez un programme, vous manipulerez les deux pièces du dispositif de 1995 — le .class portable et la machine qui l’accueille — et le TP du cours « TP — La mécanique Java, à la main » en explicitera le fonctionnement.

La figure ci-dessous résume cette chaîne d’exécution, du source .java au bytecode .class accueilli par la JVM.

Figure 1: La chaîne javac, bytecode, JVM

Sunrise, Oracle, 2010 : la guerre des licences

Pendant près de vingt ans, Java est la propriété de Sun Microsystems. En 2006, Sun ouvre son implémentation sous licence GPL : c’est le projet OpenJDK, qui devient la base commune de toutes les implémentations libres. En 2010, Oracle rachète Sun Microsystems — l’accord était préparé en interne sous le nom de code « Sunrise » — et acquiert de ce fait Java. La situation devient ambiguë : le code source est ouvert (OpenJDK), mais les binaires distribués par Oracle relèvent d’une licence propriétaire, et les conditions d’utilisation commerciale se durcissent par paliers.

Cette incertitude provoque une mobilisation de l’écosystème : le projet AdoptOpenJDK, devenu Eclipse Temurin, fournit des binaires libres construits depuis OpenJDK, avec une licence permissive en termes d’utilisation et de distribution — c’est la distribution que ce cours recommande. Dans le même mouvement, les API historiques de Java EE passent à l’Eclipse Foundation, et leur espace de noms est renommé de javax.* à jakarta.*, Oracle détenant les marques liées à javax.

Deux conséquences pratiques en découlent. D’abord, « installer Java » ne désigne plus un téléchargement unique mais une distribution parmi plusieurs, toutes conformes à la même spécification. Ensuite, la licence n’est pas un détail juridique lointain : c’est un risque industriel qui a failli fragmenter le langage, et nous le retrouvons plus loin dans cette leçon, dans le tableau des versions LTS.

La figure ci-dessous retrace la chronologie de ces épisodes de licence, de l’ouverture GPL de 2006 au renommage jakarta.*.

Figure 2: Chronologie des licences Java

Le train des versions

Jusqu’au milieu des années 2010, Java avançait par grandes versions espacées, au point que le langage donnait l’impression de stagner. Depuis 2017, le rythme est radicalement différent : une version sort tous les six mois, en mars et en septembre, qu’elle soit riche ou modeste. Les fonctionnalités ne sont plus promises à une grande version lointaine ; elles montent dans le train au fil de leur achèvement — et celles qui ne sont pas prêtes passent au train suivant. Les versions intermédiaires non pérennes (par exemple Java 22 en mars 2024, Java 23 en septembre 2024) ne sont maintenues que six mois.

Certaines versions sont désignées LTS (Long-Term Support) et reçoivent un support étendu : Java 8 (mars 2014), Java 11 (septembre 2018), Java 17 (septembre 2021), Java 21 (septembre 2023, supporté jusqu’en 2028) et Java 25 (septembre 2025, supporté jusqu’en 2030). Pour un projet industriel, choisir une LTS est un calcul rationnel : on obtient une fenêtre de correctifs de sécurité longue, une chaîne d’outils et de dépendances stabilisée, et l’on évite de migrer tout l’édifice tous les six mois. C’est pourquoi une part considérable du parc mondial tourne encore sur Java 8 ou 11 — d’où la réputation de retard du langage, réputation que l’évolution actuelle ne justifie plus : la langue bouge vite désormais, ce sont les déploiements qui restent prudents.

Ce régime explique un phénomène largement observé dans l’industrie : le décalage constant entre la dernière version disponible et celle qu’un projet ose employer. Notre cours fait le choix de la dernière LTS en date — Java 25 — afin d’aborder les fonctionnalités modernes sans quitter la situation réelle d’un projet professionnel. Les repères posés dans cette section constituent la référence du cours en matière de versions et de support ; les autres leçons s’y appuient plutôt que de les redéfinir.

Le tableau canonique des versions, pour les seuls repères cités ci-dessus :

Version Sortie Support
Java 8 mars 2014 LTS
Java 11 septembre 2018 LTS
Java 17 septembre 2021 LTS
Java 21 septembre 2023 LTS, support jusqu’en 2028
Java 22 mars 2024 intermédiaire (6 mois)
Java 23 septembre 2024 intermédiaire (6 mois)
Java 25 septembre 2025 LTS, support jusqu’en 2030

Le cadre technique qui en découle

Tout ce vocabulaire d’origine se matérialise en trois boîtes emboîtées. La JVM est la machine virtuelle qui exécute le bytecode. Le JRE (Java Runtime Environment) réunissait la JVM et les bibliothèques standard pour exécuter des programmes Java. Le JDK (Java Development Kit) ajoute le compilateur javac et les outils de développement : c’est lui qu’il vous faut. Pour le développement comme pour l’exécution, un JDK suffit désormais — la distinction JRE/JDK a disparu des distributions depuis Java 11, mais les trois sigles restent dans le langage courant.

Comme il existe plusieurs distributions d’un même OpenJDK (voir « Sunrise, Oracle, 2010 »), la question « quelle version tourne ici ? » se pose toujours en deux exemplaires : la version du langage et la marque de la distribution. Le réflexe professionnel est de le vérifier systématiquement :

java --version
javac --version

Dans ce carnet, seules les cellules Java sont exécutées par le noyau : ce fragment shell y est donné à titre de comparaison et n’y est pas exécuté. Chez vous, le même réflexe sera le premier geste de la séance « Mise en œuvre de Java », une fois le JDK installé.

Le choix de l’éditeur ou de l’IDE est sans effet sur le contenu de ce cours ; la seule exigence de base est un JDK valide. Retenez la chaîne complète, elle est celle de 1995 revisitée par 2010 : une spécification, plusieurs distributions, un train de versions, et un outil — SDKMan — pour coordonner l’ensemble sur votre machine.

La figure ci-dessous illustre l’emboîtement de ces trois périmètres.

Figure 3: Emboîtement JDK, JRE, JVM

Ce que vous retiendrez

Une seule idée, en fait : la syntaxe de Java est un document historique. Les contraintes de l’embarqué de 1991 ont produit les quatre grands arbitrages ; le pari de la JVM en 1995 a produit le bytecode et la JIT ; le rachat d’Oracle en 2010 a produit Temurin et les questions de licence ; le train de versions depuis 2017 a produit le dilemme LTS. La table ci-dessous vous donne, pour les principaux faits d’origine, le moment précis du cours où vous en vérifierez les effets en pratique — car l’histoire de Java n’est utile que si elle éclaire le code que vous écrirez.

Fait d’origine Choix qui en résulte Où vous le vivrez dans le cours
La JVM et le bytecode (1995) compilation en .class, exécution par la machine virtuelle « TP — La mécanique Java, à la main »
Le ramasse-miettes (arbitrage Green) vous ne libérez jamais la mémoire manuellement Séance « Les bases du langage Java »
Exceptions checkées (contrat d’appel) le compilateur exige que vous traitiez l’échec annoncé Séances « Accès aux bases de données relationnelles en Java (JDBC) » et « Exceptions en Java »
Pas d’héritage multiple de classes la composition devient le style par défaut Séances sur la réutilisation : composition plutôt qu’héritage multiple
Le train des versions record et les syntaxes récentes n’existaient pas à Java 8 Séance « Les bases du langage Java » (types record)
Guerres de licences et distributions Temurin, SDKMan, OpenJDK : « Java » n’est pas un téléchargement Cette leçon : tableau des versions LTS

Réutilisation