Introduction : un peu d’histoire

1. Java : genèse d’un nouveau langage

Le projet Java est né en 1991, dans le secret d’une équipe de Sun Microsystem. C’est cette année-là que le Green Project a démarré, dans le but d’étudier l’impact de la convergence entre les appareils ménagers (blancs et bruns) contrôlés numériquement et les ordinateurs.

Après 18 mois d’isolement total, les 13 personnes faisant partie du projet avaient fabriqué une télécommande digitale, munie d’un écran tactile graphique et animé. Cette seule télécommande était capable de contrôler tout un équipement audio et vidéo de salon. On peut d’ailleurs regretter qu’elle n’ait jamais été commercialisée, et que les constructeurs préfèrent aujourd’hui saturer leurs appareils de multiples boutons aux fonctionnements obscurs…

La raison pour laquelle cette télécommande était unique est qu’elle était programmée dans un nouveau langage, indépendant du processeur sur lequel il s’exécutait, conçu par James Gosling, un des membres du Green Project. James Gosling aimait bien le chêne qu’il voyait de la fenêtre de son bureau, et a appelé ce langage Oak. Le projet gagna alors en importance, des contacts furent pris avec des câblo-opérateurs américains, et il changea de nom pour s’appeler FirstPerson. Il ne rencontra pas de succès commercial, probablement trop en avance dans un milieu industriel qui était encore à la recherche de moyens de gagner de l’argent.

L’arrivée du protocole http et du navigateur Mosaic fut un événement déterminant pour ce projet, en 1993. L’équipe comprit que l’Internet allait être le réseau idéal pour positionner leur produit. Après quelques travaux supplémentaires, en 1995, James Gosling fit une démonstration très importante d’un navigateur capable de montrer du contenu html mélangé à des applets. Le navigateur, appelé WebRunner fut rendu disponible sur l’Internet, et rapidement le seuil psychologique des 10 mille téléchargements fut atteint.

Les choses s’enchaînèrent ensuite très vite. WebRunner devint HotJava, le site java.sun.com s’ouvrit au grand public, et l’annonce conjointe de Sun et Netscape d’intégrer la technologie Java dans leur navigateur lança définitivement le langage.

Les versions se succédèrent alors :

  • 1996 : JDK 1.0
  • 1997 : JDK 1.1
  • 1998 : JDK 1.2, appelé Java 2
  • 2000 : JDK 1.3
  • 2002 : JDK 1.4
  • 2004 : JDK 1.5, appelé Java 5
  • 2006 : JDK 1.6, appelé Java 6
  • 2011 : JDK 1.7, appelé Java 7
  • 2014 : JDK 1.8, appelé Java 8.
  • septemnbre 2017 : JDK 9.
  • mars 2018 : JDK 10.
  • septembre 2018 : JDK 11.
  • mars 2019 : JDK 12.

On remarque qu’à partir de la version 9, Java sort une version majeure tous les six mois, ce qui est le rythme de sortie maintenant adopté.

La possibilité d’exécuter le même code Java sur n’importe quelle machine, et sous n’importe quel système d’exploitation a été le premier souci des concepteurs de ce langage. L’objectif était de pouvoir télécharger des applets Java au travers de l’Internet, et de les exécuter dans son propre navigateur, la compatibilité absolue était donc nécessaire.

Il est donc possible de compiler un programme Java sur une machine sous Windows, et d’exécuter ce code binaire sur un Macintosh. Voici une liste (non exhaustive !) des systèmes pour lesquels des machines Java existent : PC Windows et Linux, toute machine Unix, DEC sous VMS, IBM sous MVS, téléphones portables, PDA (PocketPC Palm OS), processeurs embarqués, cartes à puce.

On peut se demander pourquoi un tel délai a été nécessaire pour produire Java 7. Finalement, de la version 2 à la version 6, Sun a réussi à conserver un rythme régulier d’une version majeure tous les deux ans : pourquoi quatre années entre Java 6 et Java 7.

En fait la raison est assez simple. En 2009 Sun, société en graves difficultés financières depuis plus d’un an, annonce son rachat par Oracle. Cette procédure de rachat va en fait durer plus d’un an, durant lequel les développement de la plateforme Java ne progressent quasiment pas.

Au premier degré, le rachat perturbe le rythme des sorties : la version 7 ne sort qu’en 2011, soit après cinq longues années d’attente. Techniquement la période n’est pas la bonne : en 2005 le débat « comment intégrer les lambdas expressions » en Java s’ouvre. La question n’est pas d’intégrer ou non cette technologie, mais porte bien sur le comment. Le délai dans la sortie de la version 7 met en sommeil ce débat, et cette intégration également. Rétrospectivement, ce retard n’est peut-être pas une si mauvaise chose, cela aura probablement permis de ne pas tomber dans certains écueils.

En 2010, Oracle, nouvellement propriétaire de Sun, et donc de Java, se retrouve donc avec un problème à résoudre : quelle sera la prochaine version majeure de Java ? Et surtout : que mettra-t-on dans cette version ? Voilà quatre ans qu’il n’y a pas eu de version majeure, le projet Lambda est dans l’ornière, et les rumeurs commencent à courir : « Java est en train de devenir le nouveau Cobol », c’est-à-dire un langage du passé, condamné à disparaître à plus ou moins long terme.

La réponse d’Oracle a été de sortir une version 7, avec ce qui était alors prêt à être publié. Donc une version 7 sans le projet Lambda. Cette version contient malgré tout de nombreux ajouts très intéressants : quelques modifications du langage, des mises à jour des API existantes, une nouvelle API I/O, un framework de calcul parallèle. Mais l’attente est déçue : les lambdas sont reportés pour la version 8, plus tard. La réaction de la blogosphère est lapidaire : Java 7 sans les lambdas déçoit.

Un deuxième événement vient perturber les choses. 2012 devait être l’année de l’intégration du projet Lambda dans Java 8. Elle sera l’année de la correction de problèmes de sécurité dans Java 6 et 7. Ces problèmes arrivent jusque dans la presse grand public, l’image de Java est atteinte, et Oracle doit consacrer toutes les ressources disponibles aux correctifs. Java 8 prend du retard, la date de sortie annoncée est repoussée de 6 mois.

C’est donc en 2014 que Java 8 sort. On peut considérer que l’intégration du projet Lambda, initialement prévue probablement pour 2008 a donc pris 6 ans de retard. Considérable pour un langage informatique.

La possibilité d’exécuter le même code Java sur n’importe quelle machine, et sous n’importe quel système d’exploitation a été le premier souci des concepteurs de ce langage. L’objectif était de pouvoir télécharger des applets Java au travers de l’Internet, et de les exécuter dans son propre navigateur, la compatibilité absolue était donc nécessaire.

Il est donc possible de compiler un programme Java sur une machine sous Windows, et d’exécuter ce code binaire sur un Macintosh. Voici une liste (non exhaustive !) des systèmes pour lesquels des machines Java existent : PC Windows et Linux, toute machine Unix, DEC sous VMS, IBM sous MVS, téléphones portables, PDA (PocketPC Palm OS), processeurs embarqués, cartes à puce.

1. Un premier exemple

1.1. Aperçu général, cycle de vie

Avant de pouvoir travailler sur des fichiers Java, il faut s’assurer qu’un environnement de développement a bien été installé sur la machine sur laquelle on se trouve. Qu’il s’agisse d’une machine Windows, Linux, d’un Macintosh ou d’une station de travail, on doit avoir un environnement « JDK » (Java Development Kit) quelque part sur sa machine. Dans le répertoire racine de l’installation de ce « JDK » se trouve un répertoire bin, dont le contenu doit être accessible via la variable PATH, sous Unix, Linux ou Windows.

Un programme Java est tout d’abord écrit sous forme de code source dans un ou plusieurs fichiers de type texte. Par convention, ces fichiers portent l’extension .java. Nous verrons dans la suite qu’il existe au moins deux autres types de contraintes sur le nommage de ces fichiers, ainsi que la façon dont on les range dans des répertoires.

Pour pouvoir être exécutés, ces fichiers doivent être compilés, ensemble ou séparément. Ce que l’on entend par « compilation » en Java n’est pas tout à fait la même chose que dans un langage tel que le C ou le C++. Dans ces langages, le code compilé est un code binaire, directement exécutable sur un microprocesseur, et donc dépendant de lui et de l’OS sur lequel il est appelé à fonctionner. En Java, le « code compilé » est en fait un « byte-code », code binaire exécuté dans une machine virtuelle, indépendant de l’ordinateur sur lequel il va fonctionner, et de son OS. Le compilateur est une application fournie avec le JDK, qui s’appelle javac . La commande de base pour compiler le fichier application.java est donc :

 $ javac application.java

Avant de tenter de compiler des programmes Java, il faut donc s’assurer que l’on a bien accès à l’application javac , au moins depuis son répertoire de travail.

S’il ne donne pas de messages d’erreur, javac produit alors un fichier au même endroit que application.java : application.class. C’est ce fichier qui contient le code qui va être exécuté dans la machine virtuelle Java. Ce fichier est au format binaire. Nous reverrons dans la suite que, quelle que soit la machine sur laquelle on travaille, le contenu binaire de ce fichier ne varie pas. Il ne s’agit pas d’un exécutable à proprement parler, au sens où on l’entend en C ou en C++, mais plutôt d’un code binaire, appelé « byte code », indépendant de la machine sur laquelle ce pseudo-exécutable va être utilisé. On peut donc copier ce fichier sur toute autre machine que celle sur laquelle il a été compilé, et l’exécuter. La notion de cross-compilation n’existe pas en Java.

Notons que l’on peut compiler d’un seul coup plusieurs fichiers dans un même répertoire, par la commande :

 $ javac *.java

Les fichiers .class portent les mêmes noms que les fichiers sources.

On a accès à l’ensemble des options de javac en tapant la commande :

 $ javac -h

L’exécution du programme est appelée par la commande suivante :

 $ java application

Là encore, java est une application livrée avec le JDK, qui se trouve dans le même répertoire que javac . Cette commande lance la machine virtuelle Java (JVM, Java Virtual Machine), capable d’exécuter le « byte code ».

On ne précise pas l’extension .class dans ce cas. On voit ici que application est donnée comme argument de la commande java .

Notons tout de suite que l’on peut rassembler plusieurs fichiers .class dans un fichier .zip ou .jar, et que l’on peut exécuter directement un programme en donnant comme argument le nom du fichier .zip ou .jar. Nous reverrons en détails ce mécanisme.

1.2. Un premier programme

Il est de tradition de commencer tout apprentissage d’un langage par l’écriture d’un petit programme capable d’écrire « Bonjour le monde » sur un écran. Il semble même que ce soit une caractéristique de tout langage de programmation évolué, et de nombreux frameworks. Java tombant dans cette catégorie, il paraît indispensable de sacrifier à cette tradition !

Écrivons un premier programme Java. Cela nous permettra d’une part de clarifier la procédure de compilation et d’exécution, et d’autre part d’examiner la structure générale des programmes Java.

Exemple 1. J’écris Bonjour le monde en Java !

public  class Bonjour {  
	 public  static  void main (String [] arguments) {  
		System.out.println("Bonjour le monde") ;  
		System.exit(0) ;  
	}
}

On remarque tout d’abord que le code est contenu dans un bloc : public class Bonjour { ... }. Tout comme dans d’autres langages, un code Java se découpe en blocs, délimités par des accolades.

Tout code Java doit être déclaré à l’intérieur d’une classe, et la fonction de ce bloc est de délimiter une telle classe. À l’intérieur d’une classe, on peut déclarer trois types de choses : des champs, des méthodes et des blocs.

La première déclaration que l’on trouve dans ce bloc est celle d’une méthode : public static void main(...). Cette méthode main a un statut particulier : c’est elle qui est appelée en premier quand on lance un programme Java.

À l’intérieur de ce bloc se trouvent deux commandes. La première affiche Bonjour le monde sur la console, la deuxième ferme le programme. Nous venons d’écrire notre premier programme Java.

1.3. Programmes, applets, servlets, etc…

Il existe en fait de nombreuses façons d’exécuter un programme Java. Le premier exemple que nous avons vu consiste à écrire une classe comportant une méthode main, et à l’exécuter.

L’autre façon d’exécuter un code Java, consiste à confier ce code à un container, qui va se charger de son exécution dans un environnement que l’on appellera « managé ». Il existe plusieurs types de container, et à chacun est associé un type d’application Java. On peut dresser la liste suivante :

  • une applet s’exécute dans un navigateur Web ;
  • une servlet est exécutée dans un serveur Web ;
  • un EJB (enterprise Java bean) dans un serveur JEE, aussi appelé serveur d’application.

Ces programmes, exécutés dans des containers, ne s’écrivent pas exactement de la même façon qu’une application indépendante (on dira stand-alone en anglais). En particulier, ils n’ont pas de méthode main.

Il existe de nombreuses autres contraintes, à la fois dans leur écriture et dans leur exécution, notamment du point de vue de la sécurité. Dans le cas des applets, ce point est même crucial : une applet est une application téléchargée au travers de l’Internet, qui s’exécute sur la machine d’un internaute. La machine Java doit donc vérifier de façon très stricte que cette application respecte la sécurité de la machine sur laquelle elle s’exécute.