Le langage Java a été conçu pour permettre l'exécution du même code sur diverses plate-formes. En particulier, mais pas uniquement, sur le web. Il y a plusieurs types de programmes Java, dont en particulier les applets Java, qui sont intégrées à des pages web et doivent respecter des règles très strictes pour ne pas risquer de causer des dégâts sur les machines d'innocents surfers, et les applications Java, qui fonctionnent comme d'autres programmes, en local sur une machine, et qui ne sont pas limités comme les applets. Dans les deux cas, le code Java est "compilé", mais les fichiers résultant de la compilation nécessitent encore une interprétation différente suivant chaque plate-forme: cette opération est réalisée par la JVM (Java Virtual Machine).
Il ne faut pas confondre Java et Javascript, qui sont deux langages de programmation sans aucun rapport. Javascript est un langage exécuté directement par les navigateurs web, et qui permet de manipuler des pages web entières pour ajouter de l'interactivité. A l'inverse, les applets Java s'exécutent aujourd'hui avec un plugin qui n'est pas directement intégré au navigateur, et l'action d'une applet se limite à une zone bien déterminée d'une page web. Les applications Java peuvent aussi fonctionner sans navigateur, contrairement aux applications Javascript. Alors que Java est un langage orienté-objet utilisant le concept de classes et avec un typage fort, Javascript ne possède pas le concept de classe et utilise un typage faible. Il est possible de faire de l'orienté-objet malgré tout avec Javascript, mais il n'existe pas de syntaxe permettant à tout le monde de faire cela de la même façon. Les implémentations de Javascript des navigateurs sont incompatibles les unes avec les autres, en particulier Internet Explorer avant la version 9, alors qu'une applet Java testée sur un navigateur a de bonnes chances de fonctionner avec un autre (du moment que l'on fait attention à la version de Java).
Avant de poursuivre, j'aimerais débouter certaines idées préconçues au sujet de Java:
Etant donné que la compilation de programmes Java ne produit pas des fichiers directement exécutables pour un processeur, on pourrait penser que les programmes Java sont plus lents que des programmes C ou C++, et pourtant... Des petits tests montrent que l'on obtient des performances très similaires. Ceci est dû à l'utilisation aujourd'hui systématique de compilateurs JIT (Just In Time), qui recompilent le code juste avant l'exécution, de manière à exécuter du code natif. Un autre problème vient du temps de chargement de la machine Java par un navigateur qui lance des applets: cela peut être long, mais une fois la machine Java chargée toutes les applets doivent se lancer rapidement (sur certains systèmes, la même machine Java peut être utilisée pour plusieurs applications, ce qui permet d'éviter le problème du chargement initial). D'autre part, il arrive que la création d'objets ralentisse un programme Java. Comme ces problèmes de vitesse ne concernent généralement que 1% du code, il suffit souvent d'optimiser ces 1% en limitant la création et la destruction d'objets pour obtenir un programme très rapide.
Au niveau des applications Java, les droits de l'application sont les mêmes que pour d'autres programmes, donc il n'y a aucune différence entre Java et d'autres langages. Pour les applets, la JVM vérifie toutes les instructions du code avant exécution, et bloque l'exécution du programme si une instruction n'est pas autorisée. Les seules instructions autorisées sont celles qui ne peuvent pas poser de risque d'intrusion. Par exemple, une applet n'a pas le droit d'écrire un fichier sur le disque. Il existe cependant la possibilité qu'une applet Java abuse d'un bug d'une JVM particulière pour exécuter des instructions non autorisées, mais la sécurité offerte par le langage est tout de même très bonne (par exemple, il n'est pas possible de profiter d'un dépassement de buffer avec Java, un type de faille classique)... et l'alternative aux applets pour des applications multimédia consiste à faire télécharger et exécuter des programmes, ce qui n'offre pas la même sécurité aux utilisateurs.
Il faut bien distinguer la syntaxe de Java des ensembles de classes (les JDK, Java Development Kit), mais dans les deux cas on ne peut pas dire que ce soit adapté aux débutants en informatique. La syntaxe de Java ressemble à celle du C++, elle est donc facile à apprendre pour ceux qui connaissent déjà ce langage. Les développeurs connaissant seulement le C devront surtout s'habituer à l'orienté-objet (ce qui prend un certain temps), et ceux déjà habitués à l'orienté-objet mais pas au C devront se faire à cette syntaxe qui peut être délicate à manier. Au niveau des JDK, il y a d'énormes différences entre la version 1.0 et la dernière, la version 1.7. Si les deux premières (1.0 et 1.1) sont simples et assez bas-niveau, les suivantes (notamment avec l'interface Swing) sont très complexes et avec un nombre de classes et de méthodes incomparablement plus grand. Il vaut donc mieux commencer simplement, avec la version 1.1 (qui corrige quelques gros défauts de la 1.0 sans trop alourdir le langage).
Java est un langage orienté-objet (OO). Comme pour les autres langages OO, on définit des classes pour représenter des concepts (par exemple Etudiant), et on utilise ensuite des instances de ces classes (par exemple toto, qui est un étudiant). Pour chaque classe, on définit des attributs (par exemple nom) et des méthodes (par exemple afficherLesInfos(), qui affichera les infos sur l'étudiant).
On dérive une classe en partant d'une classe donnée et en ajoutant des méthodes ou des attributs. Par exemple, la classe Etudiant pourrait dériver d'une classe Personne, qui aurait entre autres l'attribut nom. Comme cela, on pourrait mettre l'attribut nom dans la classe Personne et on n'aurait plus besoin de le répéter dans Etudiant et d'autres classes dérivant de Personne.
Le principal avantage de l'orienté-objet est d'encapsuler les différentes parties du code d'une application. Comme les différentes classes sont indépendantes et leurs interfaces (méthodes et attributs) sont définies explicitement, on évite les erreurs causées par les dépendances non explicites entre morceaux de codes dans les langages procéduraux.
Les interfaces graphiques standard de Java sont au nombre de 2: AWT, avec des composants graphiques simples, et Swing, qui comporte des composants plus "lourds" et qui est basé sur AWT. Elles ne sont pas décrites dans cette introduction car il n'est pas nécessaire d'apprendre AWT ou Swing pour utiliser SimuLab. Il existe des documentations de Sun sur le sujet, que ce soit pour AWT [màj: le lien ne fonctionne plus sur le site d'Oracle] ou Swing. Il existe aussi une interface graphique non développée par Sun, SWT, utilisable avec l'environnement Eclipse.
Chaque classe Java est enregistrée sous la forme d'un fichier, ayant le même nom que la classe. Il est important de respecter les majuscules et les minuscules, sachant que le nom d'une classe Java doit commencer par une majuscule (contrairement aux attributs, variables et méthodes).
Prenons l'exemple de la classe BonjourLeMonde. Elle doit être enregistrée dans un fichier nommé BonjourLeMonde.java.
La compilation génère des fichiers .class, par exemple BonjourLeMonde.class. La façon de compiler dépend du système d'exploitation. Le tutoriel Java de Sun indique quelques conseils pour compiler sur Windows et UNIX.
Pour organiser les classes, on les rassemble en "package". Cela permet d'encapsuler des ensembles de classes, de la même façon que l'on encapsule des attributs et méthodes dans une classe. Pour utiliser des classes d'un autre package, on utilise l'instruction import en début de fichier. L'instruction package permet de donner le nom du package de la classe définie.
Dans l'exemple de BonjourLeMonde, on utilise l'interface graphique AWT, contenue dans les packages java.awt et java.awt.event. Comme on veut pouvoir utiliser toutes les classes de ces packages sans avoir à les nommer, on utilise l'étoile:
import java.awt.*; import java.awt.event.*;
Pour réunir des classes d'un ou plusieurs packages dans un seul fichier, on utilise des archives jar. L'utilitaire jar permet de créer ces archives, de la même façon que l'on crée des archives tar sous UNIX.
Pour que la machine Java retrouve ses petits dans tous les dossiers et archives, il faut lui indiquer où chercher. La variable d'environnement CLASSPATH comporte la liste des dossiers et archives jar où les classes peuvent se trouver, sachant que l'on n'indique que la racine du chemin à chaque fois. Par exemple, si le fichier Vector.class se trouve dans les dossiers java/util (c'est la règle à suivre si Vector se trouve dans le package java.util), il faut indiquer dans le CLASSPATH le chemin vers le répertoire java, et non le chemin vers le répertoire util.
La façon de changer la variable CLASSPATH est différente suivant chaque système d'exploitation. Par exemple:
setenv CLASSPATH .:/Users/toto/dossierjava:/quelquepart/archive.jarou
CLASSPATH=.:/Users/toto/dossierjava:/quelquepart/archive.jar export CLASSPATHOn peut aussi garder cette variable d'environnement à chaque session en ajoutant cette commande dans le fichier ~/Library/init/tcsh/environment.mine sous MacOS X, ou ~/.tcshrc avec le shell tcsh, ou ~/.bashrc avec le shell bash.
Voilà comment on peut déclarer BonjourLeMonde après avoir utilisé les instructions import:
/** * Application Java qui affiche "Bonjour le monde" dans une fenêtre. * La classe BonjourLeMonde dérive de Frame (une fenêtre), * et implémente l'interface ActionListener qui permet de * gérer des actions. * * @author Damien Guillaume */ public class BonjourLeMonde extends Frame implements ActionListener { // on va ajouter les attributs et méthodes ici }
Tous les caractères entre /* et */, et ceux après // sur une ligne sont des commentaires et sont ignorés pour la compilation (ils n'en sont pas moins importants !). public signifie que cette classe pourrait être utilisée dans une autre classe (elle est "publique"). extends Frame signifie que la classe BonjourLeMonde dérive de la classe Frame. En effet, en Java, on ne doit pas écrire de "print" comme dans d'autres langages, car certains systèmes d'exploitation (comme MacOS 9) n'ont pas de fenêtre de terminal où ce texte pourrait être affiché: il faut donc au minimum ouvrir une fenêtre. Notons au passage que BonjourLeMonde est une application Java, et non une applet. Sinon, il aurait fallu dériver BonjourLeMonde de la classe Applet. Finalement, implements ActionListener signifie que BonjourLeMonde respecte l'interface ActionListener. Une interface est une classe sans attribut, et avec des définitions de méthodes mais sans implémentation pour ces méthodes. L'implémentation des méthodes doit être effectuée par la classe implémentant l'interface.
Après la ligne définissant le nom de la classe et les héritages, on trouve une liste des attributs de la classe. La valeur de ces attributs changera pour chaque instance, à moins que l'on utilise le mot-clé static, qui indique que la variable a une valeur unique pour toutes les instances de la classe. Pour BonjourLeMonde, on définit un attribut "s", instance de la classe String (en clair, s est tout simplement une chaîne de caractères):
private String s; // variable globale à la classe (mais privée: // on ne peut pas l'utiliser à l'extérieur de la classe)
Chaque classe comporte au moins un constructeur, c'est-à-dire une méthode utilisée pour créer une nouvelle instance de la classe et l'initialiser. Le nom de ces méthodes doit être le nom de la classe, et la seule différence entre les différents constructeurs, quand il y en a plusieurs, est la liste d'arguments.
Voici le constructeur pour notre exemple. Il commence par un appel du constructeur de Frame, la classe dont BonjourLeMonde est dérivée, et ajoute ses propres initialisations.
/** * Constructeur de la classe BonjourLeMonde * @param titre Le titre de la fenêtre * @param s La chaîne de caractères à afficher */ public BonjourLeMonde(String titre, String s) { super(titre); // appel du constructeur de Frame, qui prend aussi // le titre en argument this.s = s; // la variable globale s (this.s) prend la valeur // de la variable locale s passée en paramètre afficher(); }
Les applications Java doivent déclarer une méthode statique main. Pour l'exemple:
/** * Méthode principale, appelée au lancement d'une application Java * avec ses arguments */ public static void main(String[] args) { // crée une nouvelle instance de la classe BonjourLeMonde, // avec "bonjour" comme titre de la fenêtre // et "Bonjour le monde" à afficher à l'intérieur new BonjourLeMonde("bonjour", "Bonjour le monde"); }
En plus des attributs de la classe, on peut déclarer des variables à l'intérieur des méthodes. Ces variables peuvent être déclarées à n'importe quel endroit, mais elles ne sont utilisables qu'à l'intérieur du bloc dans lequel elles sont déclarées (par exemple à l'intérieur de la méthode, ou à l'intérieur d'une boucle for). On déclare une variable avec à chaque fois le type ou la classe, et le nom de la variable. Quelques exemples:
Contrairement à la plupart des langages de programmation, Java possède un mécanisme permettant de libérer l'espace mémoire occupé par les variables qui ne sont plus utilisées. Il faut donc allouer de la mémoire pour commencer à travailler avec des objets (les instances des classes), mais il n'est pas nécessaire de gérer la désallocation de la mémoire, la machine Java se chargeant régulièrement (ou quand nécessaire) de nettoyer l'espace mémoire.
Pour appeler la méthode d'une classe à partir d'une instance de cette classe, on utilise la syntaxe instance.méthode(paramètres), par exemple:
Personne p; p = new Personne(); p.setNom("toto");
Quelques exemples de boucles et conditions en Java:
int[] tableau = new int[10]; for (int i=0; i<10; i++) tableau[i] = i+1;La même chose avec un tableau de taille variable:
Vector tableau = new Vector(); for (int i=0; i<10; i++) tableau.addElement(new Integer(i+1));
int[] tableau = new int[10]; int i = 0; while (i<10) { tableau[i] = i+1; i++; }
int i = 5; boolean bouboule = false; char c; if (i == 10) c = 'a'; else if (i >= 7) { c = 'b'; bouboule = true; } else c = 'c';
Notez qu'il est nécessaire de mettre des caractères { et } pour délimiter un bloc à partir du moment où il y a plus d'une instruction dans ce bloc. Pour ceux qui ont l'habitude du Pascal ou du Fortran, attention à bien commencer les boucle avec i=0 (les indices des tableaux commencent en effet par 0 en Java). Attention aussi à ne pas confondre i=0 (i prend la valeur 0) de i==0 (qui teste si i est égal à 0).
Java a un mécanisme particulier pour gérer les erreurs. Chaque méthode peut "lancer" (throw) une "exception", qui signifie qu'une erreur est survenue pendant l'exécution de la méthode. Les méthodes lançant des exceptions doivent déclarer quels types d'erreurs (correspondant à des classes dérivées de la classe Exception) sont possibles. Quand on exécute une telle méthode, on peut éventuellement essayer (try) d'"attraper" (catch) l'erreur et de la gérer.
Par exemple, la transformation d'une chaîne de caractères en entier peut éventuellement lancer une erreur. La méthode valueOf de la classe Integer permet de faire cette transformation. Elle est déclarée ainsi:
public static Integer valueOf(String s) throws NumberFormatException
L'exception qui peut être lancée ici est NumberFormatException, qui indique un problème de formatage. On peut attrapper l'erreur comme cela:
String s = "1;2"; // mauvais formatage Integer valeurNumerique; try { valeurNumerique = Integer.valueOf(s); } catch (NumberFormatException ex) { System.err.println("Erreur de formattage: " + ex.getMessage()); }
getMessage est une méthode de Exception, qui est donc aussi utilisable pour les instances de la classe NumberFormatException étant donné que cette classe dérive de Exception. Elle renvoie le message d'erreur correspondant. System.err correspond à la sortie UNIX "stderr", qui permet d'afficher du texte sous la ligne de commande. Attention à n'utiliser ça que pour débugger, car les utilisateurs qui ne voient pas de ligne de commande pourraient ne pas voir de message d'erreur du tout avec l'exemple précédent !
Last but not least, les commentaires ! Il existe un petit utilitaire très pratique, javadoc, qui permet de générer automatiquement de la documentation HTML à partir du code Java... A condition bien sûr de respecter certaines règles pour qu'il retrouve ces commentaires dans le code. En particulier, il convient de mettre des commentaires devant chaque classe et méthode, formatés comme dans l'exemple. Tous les détails se trouvent sur le site d'Oracle. On gagne beaucoup de temps à documenter le code de cette façon.
Il existe bien sûr de nombreuses autres introductions à Java, plus complètes que celle-ci. Par exemple:
Voilà le fichier de l'exemple complet.