Les paquetages se révèlent pratiques pour l'organisation de votre travail et pour effectuer une séparation entre nos créations et les bibliothèques fournies par des tiers.
La définition d'un chemin de classes permet à la machine virtuelle de s'y retrouver dans l'ensemble des classes stockées dans différents répertoires ou sous-répertoires du système de fichiers et de lancer ainsi correctement l'application avec les différentes classes requises.
Java permet de regrouper des classes dans un ensemble (ou un paquet) appelé paquetage (package). Les paquetages se révèlent pratiques pour l'organisation de votre travail et pour effectuer une séparation entre nos créations et les bibliothèques fournies par des tiers.
La bibliothèque standard de Java est distribuée dans un certain nombre de paquetages : java.lang, java.util, java.net, etc. Les paquetages standard de Java constituent des exemples de paquetages hiérarchiques.
Tout comme les répertoires d'un disque dur, les paquetages peuvent être organisés suivant plusieurs niveaux d'imbrication. Tous les packetages standard de Java se trouvent au sein des hiérachies de paquetage java et javax.
L'utilisation des paquetages permet de s'assurer que le nom de chaque classe est unique. Supposons que deux programmeurs aient la brillante idée de fournir une classe Employé. Tant qu'ils placent leurs classes dans des paquetages différents, il n'y a pas de conflit.
Par exemple, manu.e3b.org est le nom de domaine utilisé pour enregistrer ces cours. Inversé, il devient le paquetage org.e3b.manu ou si vous désirez qu'il soit plus cours : org.manu. Ce paquetage peut encore être subdivisé en sous-paquetages tels que org.manu.cheminsclasse.
Du point de vue du compilateur, il n'y a absolument aucune relation entre les paquetages imbriqués. Par exemple, les paquetages java.util et java.util.jar n'ont rien à voir l'un avec l'autre. Chacun représente sa propre collection indépendante de classes.
Une classe peut utiliser toutes les classes de son propre paquetage et toutes les classes publiques des autres paquetages. Lorsque une classe doit atteindre une classe de son propre paquetage, vous n'avez aucune notation particulière à rajouter. Lorsque, par contre, vous avez besoin d'atteindre une classe publique d'un autre paquetage, il faut alors, d'une manière ou d'une autre, spécifier ce paquetage particulier.
Vous pouvez accéder aux classes publiques d'un autre paquetage de deux façons :
java.util.Date aujourdhui = new java.util.Date();
Cette technique est plutôt contraignante puisque vous devez donner cette précision supplémentaire en préfixe de la classe à chaque fois que vous en avez besoin. Si vous l'utiliser une dizaine de fois, vous devrez rajouter ce préfixe dix fois, ce qui peut être fastidieux à la longue et surtout, rend le code moins lisible puisque beaucoup plus imposant.
Vous pouvez importer une classe spécifique ou l'ensemble d'un paquetage. Vous placez les instructions import en tête de vos fichiers sources (mais au-dessous de toutes instructions package).
Par exemple, vous pouvez importer toutes les classes du paquetage java.util avec l'instruction suivante :
import java.util.*;
Vous pouvez alors écrire l'instruction suivante sans le préfixe de paquetage :Date aujourdhui = new Date();
Il est également possible d'importer une classe spécifique d'un paquetage :import java.util.Date;
...
Date aujourdhui = new Date();
La syntaxe java.util.* est moins compliquée. Cela n'entraîne aucun effet négatif sur la taille du code. Par contre, si vous importez explicitement des classes, le lecteur de votre code connaît exactement les classes utilisées.
Sachez toutefois que vous ne pouvez utiliser la notation * que pour importer un seul paquetage. Vous ne pouvez pas utiliser import java.* ni import java.*.* pour importer tous les paquetages ayant la préfixe java.
Le plus souvent, vous importez simplement les paquetages dont vous avez besoin, sans autre préoccupation. La seule circonstance demandant une attention particulière est le conflit de noms. Par exemple, à la fois le paquetage java.util et java.sql ont une classe Date.
import java.util.*;
import java.sql.*;
Date aujourdhui = new Date(); // Erreur -- java.util.Date ou java.sql.Date ?
Le compilateur ne peut déterminer de quelle Date vous avez besoin.
§
import java.util.*;
import java.sql.*;
import java.util.Date;
java.util.Date hier = new java.util.Date();
java.sql.Date aujourdhui = new java.sql.Date(...);
La localisation des classes dans les paquetages est le rôle du compilateur. Les bytecodes dans les fichiers de classe utilisent toujours les noms complets des paquetages pour faire référence aux autres classes.
Depuis la version 5.0 de Java, l'instruction import a été améliorée de manière à permettre l'importation de méthodes et de champs statiques, et non plus simplement des classes.
import static java.lang.System.*;
...
out.println("Bonjour à tout le monde"); // c'est-à-dire System.out
...
exit(0); // c'est-à-dire System.exit
import static java.lang.System.out;
Dans la pratique, il semble douteux que de nombreux programmeurs souhaitent abréger System.out ou System.exit. Le résultat me paraît moins clair. Mais il existe deux utilisations pratiques des imports statiques :
import static java.lang.Math.*;
...
sqrt(pow(x, 2) + pow(y, 2)); // plus facile à lire et à comprendre que
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
import static java.lang.Calendar.*;
...
if (aujourdhui.get(DAY_OF_WEEK) == MONDAY) // plus facile à interpréter que
if (aujourdhui.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) ...
Pour placer des classes dans un paquetage, vous devez mettre tout simplement le nom de votre paquetage, à la suite du mot réservé package, en haut de votre fichier source, avant le code qui définit les classes dans le paquetage.
Par exemple, le fichier Employé.java doit commencer ainsi :
package org.manu.personnels;
public class Employé {
...
}
Si vous ne mettez pas une instruction package dans le fichier source, les classes dans ce fichier source appartiendront au paquetage par défaut qui n'a pas de nom de paquetage.
javac org/manu/personnels/Employé.java
java org.manu.personnels.Employé
N'oubliez pas que le compilateur fonctionne sur les fichiers (avec les séparateurs de fichiers et une extension .java), tandis que l'interpréteur Java charge une classe (avec des séparateurs par point).
Nous avons déjà rencontré les modificateurs d'accès public et private. Les composants logiciels déclarés public sont utilisable par n'importe quel classe. Les éléments private ne sont accessible que dans la classe qui les définit. Si vous ne spécifiez pas de modificateur public ou private, un composant (classe, méthode ou attribut) est accessible à toutes les méthodes du même paquetage.
Nous venons de voir que les classes sont stockées dans des sous-répertoires du système de fichiers. Le chemin d'accès à la classe (classpath) doit correspondre au nom du paquetage. Vous pouvez aussi employer l'utilitaire JAR pour ajouter des fichiers de classe à une archive. Une archive contient plusieurs fichiers de classe et des sous-répertoires dans un fichier compressé, ce qui économise l'espace et réduit le temps d'accès.
Pour en connaître plus sur les archives.
§
Lorsque vous utilisez une bibliothèque tierce dans vos programmes, vous recevrez généralement un ou plusieurs fichiers JAR à inclure. Le JDK propose également plusieurs fichiers JAR, comme jre/lib/rt.jar, qui contient des milliers de classes de bibliothèques.
Pour rendre vos classes accessibles aux programmes, vous devez :
/home/user/répertoireclasses : . : /home/user/archives/archive.jar
C:\répertoireclasses ; . ; C:\archives\archive.jar
Dans les deux cas, le point désigne le répertoire courant.
§
Ce chemin de classe contient :
/home/user/répertoireclasses : . : /home/user/archives/' * '
ou :C:\répertoireclasses ; . ; C:\archives\*
Sous Unix, le * doit être échappé pour éviter l'expansion du shell.
§
Tous les fichiers JAR (à l'exception des fichiers .class) du répertoire archives figurent dans ce chemin de classes.
§
Les classes recherchent toujours les fichiers de bibliothèque d'exécution (rt.jar et les autres fichiers JAR dans les répertoires jre/lib et jre/lib/ext, mais pas les fichiers .class) ; vous les incluez explicitement dans le chemin de classe.
Attention : Le compilateur javac recherche toujours les fichiers dans le répertoire courant, mais l'interpréteur java n'examine le répertoire courant que si le répertoire " . " fait partie du chemin d'accès de classe. En l'absence de définition de chemin de classe, cela ne pose pas de problème, le chemin de classe par défaut est le répertoire " . ". Si vous avez défini le chemin de classe et oublié l'écriture du répertoire " . ", vos programmes seront compilés sans erreur, mais ils ne pourront pas s'exécuter.
Le chemin de classe recense tous les répertoires et les fichiers archives constituant des points de départ pour retrouver les classes :
/home/user/répertoireclasses : . : /home/user/archives/archive.jar
Supposons par exemple que le fichier source contienne les directives si dessous et que le code source fasse référence à une classe Employé :
import java.util.*;
import org.manu.personnels.*;
Le compilateur essaiera alors de trouver java.lang.Employé (car le paquetage java.lang est toujours importé par défaut), java.util.Employé, org.manu.personnels.Employé et Employé dans le paquetage courant.
Il recherche chacune de ces classes dans tous les emplacements du chemin de classe. Une erreur de compilation se produit si plus d'une classe est trouvée (les classes devant être uniques, l'ordre des instructions import n'a pas d'importance.
L'étape suivante pour le compilateur consiste à consulter les fichiers source pour voir si la source est plus récente que le fichier de classe. Si oui, le fichier source est recompilé automatiquement.
Souvenez-vous que vous ne pouvez qu'importer des classes publiques des autres paquetages. Un fichier source peut seulement contenir une classe publique, et les noms du fichier et de la classe publique doivent correspondre.
Le compilateur peut donc facilement localiser les fichiers source pour les classes publiques. Vous pouvez importer des classes non publiques à partir des paquetages courants. Ces classes peuvent être définies dans des fichiers source avec des noms différents. Si vous importez une classe à partir d'un paquetage courant, le compilateur examine tous les fichiers sources de ce paquetage pour vérifier celui qui la définit.
Mieux vaut spécifier le chemin de classe avec l'option -classpath (ou -cp) :
java -classpath /home/user/répertoireclasses : . : /home/user/archives/archive.jar MonProgramme.java
java -classpath C:répertoireclasses ; . ; C:/archives/archive.jar MonProgramme.java
L'ensemble de la commande doit être tapé sur une seule ligne. Mieux vaut également placer cette longue ligne dans un script de shell ou un fichier de lot.
La méthode préférée pour définir le chemin de classe consiste à employer l'option -classpath. Vous pouvez aussi définir la variable d'environnement CLASSPATH, selon votre shell. Utilisez la commande :
export CLASSPATH = /home/user/répertoireclasses : . : /home/user/archives/archive.jar
setenv CLASSPATH /home/user/répertoireclasses : . : /home/user/archives/archive.jar
set CLASSPATH = C:répertoireclasses ; . ; C:/archives/archive.jar
Le chemin de classe est défini tant que le shell existe.
§