2. Notion de paquet

2.1. Déclaration d’appartenance à un paquet

Déclarer qu’une classe appartient à un paquet est une chose très simple : il suffit de mettre en première ligne de cette classe la directive package.

Exemple 101. Déclaration d'un paquet dans une classe

package transport ;  // la classe Voiture appartient au paquet transport
  
 public  class Voiture {  // rangée dand un fichier transport/Voiture.java
     // corps de la classe  
}

La convention de rangement des fichiers impose que le fichier Voiture.java soit rangé dans un répertoire transport. Il est bien sûr possible de créer des sous-paquets dans des paquets parent. Il suffit pour cela de le déclarer dans la directive package. Comme on le sait, le caractère de séparation des répertoires varie avec le système d'exploitation sur lequel on travaille. Afin de s'affranchir de cette subtilité (et pour d'autres raisons), Java code le chemin complet d'un package avec le point comme caractère de séparation. Ainsi la classe suivante est-elle rangée dans le répertoire transport/motorise.

Exemple 102. Rangement d'une classe dans un sous-paquet

package transport.motorise ;   // la classe Voiture appartient au paquet transport.motorise
  
 public  class Voiture {   // rangée dand un fichier transport/motorise/Voiture.java
     // corps de la classe  
}

Notons ici que le compilateur javac ne fait pas ce rangement automatiquement, si l’on compile avec la ligne de commande, il faut le faire manuellement, en respectant bien sûr les majuscules / minuscules au niveau des noms de répertoires. Tous les outils intégrés de développement Java savent ranger les classes compilées dans la bonne structure de répertoires, cela va de soi. Le nom complet d’une classe Voiture rangée dans le paquet transport.motorise sera transport.motorise.Voiture. Le fichier compilé Voiture.class se trouvera lui dans le répertoire transport/motorise. La bibliothèque standard Java est entièrement structurée de cette façon là. Nous avons déjà plusieurs exemples de noms de classes complets : java.lang.Object ou java.lang.String. Nous savons donc maintenant que ces deux classes doivent être rangées quelque part dans un répertoire java/lang.

2.2. Chargement d’une classe

L’intérêt de pouvoir ranger ces classes dans une structure hiérarchique va au-delà de la simple gestion d’un grand nombre de fichiers .class. La distribution standard Java 1.4 comporte un peu plus de 3500 classes. Il serait très peu optimal de charger toutes ces classes systématiquement au lancement de la machine Java, surtout si l’application que l'on exécute ne doit en utiliser qu’une dizaine. Java supporte donc complètement le chargement dynamique des classes. Quand une classe est instanciée, elle est chargée en mémoire, ainsi que toutes les classes dont elle a besoin pour fonctionner, notamment celles dont elle hérite. Ces 3500 classes étant rangées dans quelques centaines de répertoires, il serait également trop lourd pour le système d’examiner systématiquement le contenu de toute la hiérarchie à la recherche de quelques classes données. Il existe donc un système pour indiquer à la machine Java les répertoires où elle doit aller chercher ses classes dans la hiérarchie. Ce système consiste à utiliser la directive import.

Exemple 103. Directive import sur un package complet

package vehicule.motorise ;  
      
 import java.util.* ;  // importation de tout le package java.util, dont la classe Date  
      
 public  class Voiture {  
      
     private Date dateAchat ;  
      
}

Cette directive indique à la machine Java qu’elle doit aller regarder dans le répertoire java/util pour trouver les classes utilisées dans ce fichier source. Si elle rencontre une classe inconnue, et qu'elle ne la trouve pas dans java/util, elle génèrera une erreur à la compilation. Remarquons qu'avec cette directive d'import, la machine Java ne cherche les classes que dans le répertoire java/util, elle ne descend pas dans les sous-niveaux d’arborescence. Dans notre exemple, il existe bien une classe Date dans la package java.util, donc la compilation pourra se faire. Notons également que l'on aurait pu importer la classe Date explicitement avec la directive suivante.

Exemple 104. Directive import sur une classe particulière

package vehicule.motorise ;  
      
 import java.util.Date ;  // importation d'une classe unique  
      
 public  class Voiture {  
      
     private Date dateAchat ;  
      
}

Le répertoire java.lang est toujours importé par défaut, il n’y a pas besoin de le déclarer explicitement. Il contient toutes les classes de base, dont la classe Object.

2.3. Choix de nom

2.3.1. Nom d'une classe

Le nom d’une classe reste le nom qui est déclaré dans la directive public class, mais on ne peut y accéder que grâce à son chemin complet, tout comme un fichier. Cela signifie qu’il est possible d’avoir deux classes qui portent le même nom en deux endroits différents de la hiérarchie. Si les deux répertoires sont chargés il y aura conflit de nom. Afin de lever ce conflit, il est possible d'appeler chaque classe par son chemin complet. Par exemple java.util.Date et java.sql.Date peuvent être appelées de cette façon dans un même fichier source, sans qu’il y ait de conflit.

Exemple 105. Utilisation de deux classes portant le même nom dans le même fichier

public  class Voiture {  
      
     private java.util.Date dateAchat ;  
     private java.sql.Data dateMiseEnCirculation ;  
      
}

Même s'il reste rare d'avoir besoin de deux classes portant le même nom dans une même classe, on rencontre tout de même ce cas dans les applications réelles.

2.3.2. Nom d'un package

Il est bien sûr possible d’ajouter des classes dans une hiérarchie existante de packages. Il est possible aussi de créer de nouvelles branches d'une hiérarchie existante. Il est cependant dangereux de nommer ses branches au hasard ou avec des noms trop génériques, surtout si les classes développées ont elles aussi des noms génériques. Les spécifications de Java préconisent donc un pattern pour nommer sa hiérarchie, que tout le monde respecte, y compris et surtout, les éditeurs de logiciels. Cette convention consiste, pour une société de développement, à prendre son nom de domaine lu à l’envers comme base de sa hiérarchie (quelle société d'informatique ne possède par son nom de domaine ?). Ainsi, IBM, propriétaire de ibm.com, placera les classes de ses applications et bibliothèques dans une hiérarchie com.ibm. De même pour Sun, qui utilise (ou utilisait) com.sun, ou Oracle, qui utilise com.oracle, et qui peut maintenant utiliser aussi com.sun. Tous les programmeurs Java utilisent cette convention, qui permet automatiquement de gérer les conflits de noms en interne des entreprises de développement. Il en va de même pour les très nombreux logiciels open-source, qui diffusent tous des packages portant leur nom : org.hibernate, org.apache, net.sourceforge. La bibliothèque standard utilise deux hiérarchies qu’il est interdit de modifier : java. et javax.. La hiérarchie java. est utilisée pour la bibliothèque standard, javax. pour les extensions de la bibliothèque standard. Toute machine Java possède l’intégralité de la bibliothèque standard, mais elle n’est pas tenue de posséder toutes les extensions. Il existe une exception à cette règle, ce sont les machines Java utilisées dans le domaine des terminaux mobiles : PDA, smartphones et téléphones mobiles. L’édition Java pour ce type de terminal est J2ME : Java 2 micro édition. La J2ME définit deux types de configurations : la CDC (Connected Device Configuration) et la CLDC (Connected Limited Device Configuration). La CDC s’adresse plutôt aux PDAs haut de gamme. Dans la mesure où les espaces mémoire dans lesquels fonctionnent ces JVM sont parfois très restreints, la librairie standard est réduite. La configuration CDC se fonde sur une machine Java appelée CVM, qui supporte les paquets util, io, net, security, text et une partie de lang. CLDC se fonde elle sur la KVM, qui ne supporte qu’ util et io, une partie de lang, et des classes spécifiques, rangées dans javax.microedition. Des paquets optionnels sont disponibles pour les deux constructions. Notons qu'une machine KVM est capable de se lancer dans seulement 40ko de mémoire !
Java langage & API
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
Introduction : un peu d'histoire
1. Java : genèse d'un nouveau langage
Programmer en Java
1. Un premier exemple
1.1. Aperçu général, cycle de vie
1.2. Un premier programme
1.3. Programmes, applets, servlets, etc...
2. Une première classe
2.1. Écriture d'une classe
2.2. Instanciation de cette classe
3. Types de base, classes et objets
3.1. Types de base
3.2. Classes et objets
Classes Object et String
1. Introduction
2. La classe Object
2.1. La méthode toString()
2.2. La méthode clone()
2.3. La méthode equals()
2.4. La méthode hashCode()
2.5. La méthode finalize()
2.6. La méthode getClass()
3. La classe String
3.1. Introduction
3.2. Construction d'un objet de type String
3.3. Concaténation, StringBuffer et StringBuilder
3.4. Concaténations de chaînes de caractères depuis Java 8
3.5. Concaténations de chaînes de caractères depuis Java 11
3.6. Extraction d'une sous-chaîne de caractères
3.7. Comparaison de deux chaînes de caractères
3.8. Méthodes de comparaisons lexicographiques
3.9. Méthode de recherche de caractères
3.10. Méthode de modification de chaîne
3.11. Méthode de duplication
3.12. Support de l'unicode, internationalisation
Structure d'une classe
1. Introduction
2. Classes
2.1. Classes publiques
2.2. Classes internes
2.3. Classe membre
2.4. Classes locales
2.5. Classes anonymes
2.6. Le mot-clé this
3. Éléments statiques
3.1. Champ statique
3.2. Cas des constantes
3.3. Bloc statique
3.4. Classe membre statique
4. Membres d'une classe, visibilité
4.1. Bloc non statique
4.2. Accès à un membre, visibilité
4.3. Les champs
4.4. Signature d'une méthode
4.5. Les méthodes
4.6. Getters et Setters
5. Constructeur, instanciation
5.1. Chargement d'une classe
5.2. Constructeurs d'une classe
5.3. Instanciation d'un objet
5.4. Destruction d'objets
5.5. Le mot-clé final
6. Énumérations
6.1. Déclaration d'une énumération
6.2. Classe énumération
6.3. Méthode toString()
6.4. Méthode valueOf()
6.5. Méthode values()
6.6. Méthode ordinal()
6.7. Méthode compareTo()
6.8. Constructeurs privés
6.9. Classe utilitaire : EnumSet
Noms, opérateurs, tableaux
1. Introduction
2. Identificateurs, noms et expressions
2.1. Identificateurs et noms
2.2. Expressions
3. Opérateurs, ordre d'exécution
3.1. Ordre d'exécution
3.2. Les opérateurs
3.3. Les opérateurs ++ et --
3.4. Les opérateurs % et /
3.5. Les opérateurs <<, >> et >>>
3.6. L'opérateur instanceof
3.7. Les opérateurs &, | et ^
3.8. Les opérateurs && et ||
3.9. L'opérateur ? ... :
3.10. Les opérateurs d'affectation
4. Tableaux
4.1. Création d'un tableau
4.2. Initialisation d'un tableau
4.3. Utilisation d'un tableau comme un Object
4.4. Tableaux de tableaux
4.5. Copie de tableaux
5. Blocs, boucles et contrôles
5.1. Blocs
5.2. Mots-clés réservés
5.3. Tests : if et switch
5.4. Boucles : for, while, do ... while
5.5. Commandes continue et break
5.6. Commandes return et goto
Nombres, précision, calculs
1. Introduction
2. Calculs
2.1. Précision
2.2. Codage des nombres flottants
2.3. Le mot-clé strictfp
2.4. Conversion de types
3. Dépassements de capacité
3.1. Cas des entiers
3.2. Cas des flottants
3.3. Bibliothèques BigInteger et BigDecimal
4. Fonctions mathématiques
4.1. Fonctions usuelles
4.2. Générateurs aléatoires
5. Classes enveloppe
5.1. Associer les types de base à des objets
5.2. Auto-boxing
Héritage, abstraction, interfaces
1. Introduction
2. Abstraction et encapsulation
2.1. Abstraction
2.2. Encapsulation
3. Héritage
3.1. Définition de l'héritage
3.2. Conséquences pour les membres
3.3. Polymorphisme
3.4. Empêcher l'héritage
4. Classes abstraites
5. Interfaces
5.1. Introduction
5.2. Définition
5.3. Java 8 et les interfaces
5.4. Utilisation des interfaces
5.5. Définition de constantes dans les interfaces
5.6. Utilité des interfaces
Packages
1. Introduction
2. Notion de paquet
2.1. Déclaration d’appartenance à un paquet
2.2. Chargement d’une classe
2.3. Choix de nom
3. Archives, chemin de recherche, classpath
3.1. Archives
3.2. Variable CLASSPATH
3.3. Notion de classloader
3.4. Bilan sur les classes chargées
3.5. Visibilité
3.6. Conseils d'écriture, bibliothèque standard
Exceptions
1. Introduction
2. Erreurs et Exceptions
2.1. Classe Throwable, notion de stack trace
2.2. Classe Error
2.3. Classe RuntimException
2.4. Classe Exception
3. Déclenchement d'une exception
3.1. Exceptions déclenchées par la JVM
3.2. Exceptions déclenchées par l'application
4. Capter une exception
4.1. Traiter une exception localement
4.2. Code de captage
5. Créer ses propres exceptions
Entrées / sorties
1. Introduction
2. Notion de fichier
2.1. Introduction
2.2. La classe File
2.3. Construction d'une instance de File
2.4. Méthodes exposées
3. Flux de sortie
3.1. Introduction, notion de flux
3.2. Écriture de caractères, classe Writer
3.3. Bufferisation, construction d'un flux sur un autre
3.4. Utilisation de PrintWriter
3.5. Écriture d'octets, OuputStream
3.6. Écriture de types primitifs : DataOutputStream
3.7. Écriture d'objets : ObjectOutputStream
4. Flux d'entrée
4.1. Introduction
4.2. Lecture de caractères, classe Reader
4.3. Bufferisation, lecture ligne par ligne
4.4. Lecture d'octets : InputStream
4.5. Lecture de types primitifs : DataInputStream
4.6. Lecture d'objets : ObjectInputStream
5. Lecture et écriture de flux croisés
5.1. Introduction
5.2. Lire des caractères sur un flux binaire : classe InputStreamReader
5.3. Écrire des caractères sur un flux binaire : classe OutputStreamWriter
5.4. Remarque sur les jeux de caractères
6. Serialization d'objets
6.1. Enjeu de la sérialization d'objets
6.2. Serialization d'un objet
6.3. Sérialization d'une grappe d'objets
6.4. Première surcharge : méthode writeObject() readObject()
6.5. Deuxième surcharge : utilisation d'un externalizer
6.6. Troisième surcharge : utilisation d'un objet proxy
7. Flux compressés
7.1. Introduction
7.2. Flux de type gzip
7.3. Flux de type zip