Travaux Pratiques n°1 (Java)

Objectifs (2h)

  • Savoir compiler un code Java
  • Savoir documenter un code Java
  • Savoir exécuter un code Java
  • Savoir lire les messages d'erreur Java
  • Savoir parcourir et utiliser l'API du SDK

Présentation générale de Java et du Standard Development Kit (SDK)

Le SDK regroupe un ensemble d'outils permettant principalement de compiler et d'exécuter des programmes Java. Actuellement, la dernière version stable est Java SE 1.7.0 et est téléchargeable sur le site Java SE d'Oracle.

Java est un langage interprété, ce qui signifie qu'un programme compilé n'est pas directement exécutable par le système d'exploitation mais il doit être interprété par un autre programme, qu'on appelle interpréteur.

interprete.png

Le compilateur javac

javac est le compilateur fourni dans le SDK permettant de compiler un fichier source .java. Ce code source est alors compilé par le compilateur javac en un langage appelé bytecode et enregistre le résultat dans un fichier dont l'extension est .class. Pour compiler une classe Livre contenue dans un fichier Livre.java on exécutera donc l'instruction suivante :

javac Livre.java

L'interpréteur java et la machine virtuelle

Le bytecode n'est pas directement exécutable. Il doit être interprété par une machine virtuelle Java qui le traduit en un langage adapté au système d'exploitation. Le programme java fourni dans le SDK lance une machine virtuelle pour interpreter une classe compilée. Attention : l'extension .class ne doit pas être précisée dans l'instruction d'interprétation. Pour exécuter la classe Livre compilée dans un fichier Livre.class, on écrit donc :

java Livre

Séparation des sources et des classes compilées

En général, les fichiers contenant le code source des classes sont séparés des fichiers contenant le bytecode pour mieux structurer les différentes parties du programme. Pour que les fichier créés par une compilation le soient dans un autre répertoire on utilise l'option -d. Par exemple :

javac -d /MonRepertoire/Java/classes Livre.java

Lorsque le bytecode ne se situe pas dans le répertoire courant, il faut signaler à la commande java où il se trouve grâce à l'option classpath. Par exemple :

java -classpath /MonRepertoire/Java/classes Livre

Exercice 1

Créez un répertoire tp1 (avec la commande md sous Windows, mkdir sous Linux ou avec l'exploreur de votre choix). Nous allons mettre les sources dans un répertoire nommé src et les .class dans un répertoire nommé bin (à créer comme précédemment). Ouvrez un éditeur de texte, puis créez un fichier vide nommé HelloWorld.java dans le répertoire tp1/src.

Exercice 2

Ecrivez le code source d'une classe HelloWorld dans le fichier HelloWorld.java. La classe HelloWorld contient un attribut privé message qui est du type String. HelloWorld doit également contenir deux méthodes : un constructeur qui attribue une valeur au message et une méthode public String getMessage() qui renvoie la valeur du message. Compilez cette classe en séparant bien le code source et le bytecode grâce à la commande suivante, depuis le répertoire tp1 :

javac -d bin src/HelloWorld.java

Ceci signifie que la classe à compilée est dans le fichier HelloWorld.java qui est dans le répertoire src et que le fichier .class doit être généré dans le répertoire bin, comme dans la figure suivante :

arborescence.png

Classe exécutable et méthode main()

La classe HelloWorld n'est pas exécutable dans son état actuel. Une classe exécutable est une classe qui contient une méthode spécifique (main) utilisée comme point de départ de l'exécution. La méthode main d'une classe s'écrit de la manière suivante :

public static void main(String[] args){
   //début du code à exécuter
}

Exercice 3

Ajoutez une méthode main à la classe HelloWorld. Dans cette méthode créez une instance de HelloWorld et affichez son message. Pour afficher du texte à l'écran utilisez la méthode System.out.println(String t). Compilez comme précédemment.

Savoir lire les messages d'erreur Java

Java est très bavard lorsqu'il vous signale une erreur d'exécution. Il affiche notamment ce qu'on appelle une trace de la pile d'appels qui ont amené à cette erreur. Cette liste d'appels est affichée de l'appel le plus interne à l'appel le plus externe. Pour trouver l'origine de l'erreur (si elle est dûe à votre code), parcourez cette liste jusqu'à trouver une classe de votre propre code. Le message d'erreur Java affiche également la ligne du code de l'appel. Pratique pour directement aller à la source de l'erreur. De plus, Java faisant usage de la notion d'exception, en lisant le type d'exception qui a généré l'erreur il est également possible d'en déduire l'origine. Par exemple, si NullPointerException est affichée, il y a de forte chance que vous ayez oublié d'initialiser un objet (par appel à new) avant de faire appel à un de ses attributs ou méthodes.

Exercice 4

Exécutez la classe HelloWorld grâce à la commande suivante :

java -classpath bin HelloWorld

car le fichier .class à exécuter ne se situe pas dans le répertoire courant.

Les arguments de la méthode main

Vous avez vu que la méthode main possédait la signature suivante :

public static void main(String args[]) {
   ...
}

Le tableau args contient tous les arguments passés en paramètres du programme Java. Par exemple, suite à la ligne de commande

java MonProgramme texte1 127 unfichier.txt

le tableau args contiendra les élément suivants :

  • args[0] : la chaîne de caractères "texte1"
  • args[1] : la chaîne de caractères "127"
  • args[2] : la chaîne de caractères "unfichier.txt"

Packages

Notion de package et de classpath

Un package est un regroupement de plusieurs classes selon un thème précis. Au niveau du code source, un package n'a pas d'existence explicite, il n'est réellement créé que dès que sa première classe est compilée. Pour indiquer qu'une classe appartient à un package on utilise au tout début du fichier contenant le code source le mot-clé package . Pour assigner la classe Livre au package biliotheque, on écrira :

package bibliotheque;
public class Livre {
   //contenu de la classe
}

Un package peut être imbriqué dans un package plus général et ainsi de suite (comme des dossiers). Pour désigner un package document imbriqué dans le package bibliotheque on écrira par exemple:

package bibliotheque.document;

A la compilation, des répertoires sont créés pour correspondre aux packages des classes compilées. Les fichiers contenant le bytecode sont enregistrés dans le répertoire de leur package. Une classe est alors désignée par son nom de package, suivie d'un point puis du nom de la classe. Par exemple, pour exécuter la classe Livre on utilise l'instruction:

java bibliotheque.Livre

Pour que la référence à la classe Livre soit effective, il faut que le répertoire contenant son package soit accessible par la machine virtuelle. Il y a deux solutions pour cela:

  1. Exécuter la classe à partir du répertoire contenant le répertoire bibliotheque. Dans ce cas là, le package bibliotheque et les classes qu'il contient sont directement trouvés car ils sont dans le répertoire d'exécution.
  2. Exécuter la classe à partir d'un autre répertoire. Il faut alors préciser le chemin d'accès du répertoire contenant le package bibliotheque. Cela se fait en utilisant l'option -classpath comme précédemment :
java -classpath /MonRepertoire/Java/classes bibliotheque.Livre

Importation de package

Quand une classe fait référence à une autre classe, elle doit l'importer sauf si elles se trouvent dans le même package. Pour cela le mot-clé import est utilisé au début du fichier source, un peu comme include de C, comme dans l'exemple qui suit:

package client;

import bibliotheque.Livre;

public class Lecteur {
   Livre[] emprunte;
   ...
}

De cette manière la classe Lecteur peut utiliser la classe Livre. Il est également possible d'importer toutes les classes d'un package en utilisant le symbole * comme suit:

import bibliotheque.*;

Exercice 5

Créez un package fr.emse.helloworld (donc un répertoire fr/emse/helloworld dans le répertoire src) et déplacez-y votre fichier HelloWorld.java. Déclarez que la classe HelloWorld appartient au package fr.emse.helloworld.

Compilez avec la commande suivante :

javac -d bin src/fr/emse/helloworld/HelloWorld.java 

Puis exécutez-la avec la commande suivante :

java -classpath bin fr.emse.helloworld.HelloWorld

Vous devriez obtenir l'arborescence suivante :

arborescence1.png

Documentation

L'outil javadoc

javadoc est un autre programme du SDK qui créé une documentation automatique à partir du code source de classes Java. Cette documentation automatique décrit les membres d'une classe dans un format HTML. Pour créer tous les fichiers de documentation d'un package, on utilise javadoc comme suit:

javadoc -d <répertoire de destination> <nom des packages>

Le fichier principal de documentation est index.html créé dans le répertoire de destination.

Remarque : par défaut la commande javadoc ne généère la documentation que pour les classes et membres public, donc pensez à établir la visibilité de votre classe à public. Vous pouvez également spécifier à javadoc que vous voulez également générer la documentation pour les membres privés avec l'option -private :

javadoc -d <répertoire de destination> -private <nom des packages>

Exercice 6

Générez la documentation de votre package dans un répertoire tp1/doc. Vous devriez obtenir l'arborescence suivante :

arborescence2.png

Quelques tags javadoc

javadoc peut interpréter des commentaires spécifiques introduits dans le code source pour enrichir la documentation générée. Ces commentaires se situent juste avant la déclaration d'une classe, d'un attribut ou d'une méthode. Ils commencent par /** et se terminent par */. Ces commentaires contiennent une partie textuelle libre et des tags interprétés pour certains commentaires spécifiques.

Quelques tags fréquemment utilisés

  • @author : l'auteur d'une classe
  • @version : le numero de version d'une classe
  • @see : une référence à une classe ou un membre d'une classe intéressant pour la classe ou la méthode commentée
  • @param x : Une description du paramètre d'entrée x d'une méthode
  • @return : Une description de la valeur renvoyée par une méthode

Exemple de classe commentée pour javadoc

package bibliotheque;
/**
 * Cette classe est utilisée pour représenter un livre.
 *
 * @author Laurent Vercouter
 */
public class Livre extends Produit {

   /**
   * Le titre du livre
   */
   private String titre;

   /**
   * L'auteur du livre
   */
   private String auteur;

   /**
   * Le constructeur de la classe Livre
   *
   * @param tit Le titre du livre
   * @param aut L'auteur du livre
   */
   public Livre(String tit, String aut) {
       titre = tit;
       auteur = aut;
   }

   /**
   * Cette méthode renvoie une chaîne de caractères qui décrit
   * textuellement le livre (par son titre, son auteur et l'éditeur)
   *
   * @return Une chaîne de caractère décrivant le livre
   */
   public String description() {
       return "\""+titre+"\" de "+auteur+" edite par "+editeur;
   }
}

Exercice 7

Ajoutez des commentaires et des tags pour que vos classes soient correctement documentées. Par la suite, toutes vos classes devront être commentées de cette manière.

l'API du SDK

Une bonne documentation est indispensable pour faciliter l'usage des classes développées (même quand on les a soi-même écrites !). Un très grand nombre de classes générales ont déjà été écrites par d'autres développeurs et il est souvent très utile de les ré-utiliser plutôt que de tout ré-implémenter. Sun fournit ainsi de nombreuses classes générales dans une API (Application Programmer Interface). Ce sont des outils allant de la simple sortie standard sur la console jusqu'au chargement d'image, gestion du réseau, GUI, … La plupart de ces classes sont indépendantes du type d'application développées et sont utilisables dans de nombreux projets.

La documentation de l'API du SDK est alors un outil essentiel pour tout programmeur Java car elle permet de comprendre ce que fait une classe et comment bien l'utiliser. Elle est consultable en ligne sur le site Java SE d'Oracle.

Tous les packages de l'API du SDK 1.7.0 y sont décrits. Pour trouver s'il existe une classe pour un problème spécifique, on parcourt en général le package correspondant à cette spécificité. Les packages les plus simples et les plus fréquemment utilisés sont :

  • java.lang : classes de base du langage
  • java.util : classes "utilitaires" (pour des problèmes fréquents comme la manipulation d'ensembles, …)
  • java.io : classes pour la gestion d'entrées/sorties
  • java.net : classes pour la programmation réseau
  • java.awt et javax.swing: classes pour la programmation d'interfaces graphiques

Consultons par exemple la documentation associée à la classe java.lang.String:

  1. Cliquez dans la zone en haut à gauche sur le package java.lang. Ce clic a pour effet d'afficher seulement le contenu du package dans la zone en bas à gauche.
  2. Cliquez sur la classe String pour afficher la documentation associée, dans la partie centrale du navigateur.
  3. Consultez la documentation de la classe pour constater que la première partie est un descriptif général de la classe, puis suit la liste des champs, constructeurs et méthodes. Un descriptif complet de chaque membre de la classe est accessible par un clic sur son nom.

Exercice 8

Identifiez, dans la classe java.lang.String, une méthode pour remplacer dans une chaîne de caractères chaque apparition de l'expression "abc" par l'expression "de".

Exercice 9

L'objectif de cet exercice n'est pas de produire du code Java mais d'apprendre à naviguer dans la documentation de l'API.

  1. Trouvez une classe puis une de ses méthodes pour tester si un fichier existe à partir de son chemin d'accès (répertoire + nom du fichier).
  2. Trouvez une classe permettant d'obtenir aléatoirement un nombre entier. Trouvez aussi la méthode requise pour cette opération.
  3. Trouvez une classe pour décrire une exception qui apparaît lors d'une erreur de conversion d'une chaîne de caractères en une valeur numérique.

Collections

Une collection est un objet qui référence plusieurs objets. La plupart des classes représentant des collections sont contenues dans le package java.util. Les classes de type collection implémentent des méthodes similaires pour accéder au groupe d'objets référencés. Les principales méthodes sont :

  • add pour ajouter un objet à la collection
  • contains pour tester si un objet appartient à la collection
  • remove pour retirer un objet d'une collection
  • size pour connaître la taille d'une collection

S'il existe différentes classes de collection, c'est parce que chacune présente des propriétés spécifiques :

  • Les ensembles qui regroupent des objets distincts de manière non ordonnée. Par exemple, la classe java.util.HashSet.
  • Les listes qui regroupent des objets selon un ordre donné. Par exemple, la classe java.util.Vector.
  • Les maps qui associent à chaque objet du groupe une clé pour accéder directement à un objet. Par exemple, la classe java.util.Hashtable.

Depuis la version 1.5 du SDK, les collections sont typées, c'est-à-dire qu'on doit désormais préciser la classe des objets présents dans une collection. Par exemple, pour déclarer puis instancier une liste de chaînes de caractères, on écrira :

Vector<String> maListe;
maListe = new Vector<String>();

Le parcours d'une collection peut s'écrire de manière simplifiée dans une boucle for. Il suffit de référencer le type d'objets de la collection avec un nom de variable comme suit :

for (String s: maListe) {
   System.out.println(s);
}

Ce qui signifie ici : pour chaque String (que l'on nomme s) contenu dans maListe, on écrit l'objet sur la sortie standard. Cette boucle comptera autant d'itérations qu'il y a d'éléments dans maListe et à chaque itération, s aura pour valeur un élément de maListe.

Exercice 9

Modifier la classe HelloWorld pour qu'elle encapsule (i.e. contienne un attribut) et affiche un ensemble de messages. Utilisez la classe java.util.Vector pour faire référence à plusieurs chaînes de caractères.

Exercice 10

Ajouter à la classe HelloWorld une méthode String getMessage(int i) permettant de récupérer le ième message de la liste et une méthode void addMessage(String message) pour ajouter un message en fin de liste.

Exercice 11

Modifiez votre main pour récupérer les messages à partir des arguments du main et pour afficher tous les messages contenus dans l'instance de la classe HelloWorld.

Rendu du travail

Le TP doit être rendu en fin séance, quel que soit son état d'avancement. Envoyez le source des classes (.java) ainsi que les classes compilées (.class) dans une archive zip (veiller à bien ajouter les sources !) à Gauthier Picard à la fin, avec comme sujet [LCPOO] TP1a <nom1> <nom2>. L'absence d'envoi sera sanctionnée dans la note finale.


Gauthier Picard