Les spécificateurs sont les mots clés associés aux classes, variables et méthodes pour leur octroyer certaines propriétés. Ils sont utilisés pour limiter les accès ou pour attribuer des possibilités ou restrictions spécifiques: statique, constant, non-modifiable, privée... pour une variable ou une méthode, non dérivable, abstraite... pour une classe. Ce chapitre abordera ensuite les packages qui permettent de constituer les arborescences des applications.
Les spécificateurs sont des mots clés qui définissent tour à tour les droits d'accès d'une variable, d'une méthode ou leur caractère statique. Ils se placent en tête de code. C'est donc le premier élément qui est écrit lors des définitions des variables ou des méthodes:
Spécificateur(s) Type
variable;
Spécificateur(s) Type_Renvoyé méthode
( arguments );
Exemple :
static final String sTest="toto";
private Integer;
|
Propriété |
Méthode |
Classe |
public |
La variable est visible par n'importe quelle classe du package ou extérieure au package. |
La méthode est visible de n'importe quelle classe. |
Peut être dérivée par des classes de tout package |
private |
La variable n'est visible que dans la classe courante. ( non visible même des classes filles ) |
La méthode n'est applicable que dans la classe courante. |
Pas de sens |
protected |
Le variable n'est visible que par la classe, ses filles et les autres classes du package ainsi que leurs filles |
La méthode n'est utilisable que par la classe, ses sous classes du même package ou de packages différents et les autres classes du package ainsi que leurs filles |
Pas de sens |
static |
La
variable est dite "variable de classe".
|
La méthode
est dite "méthode de classe" . Elle est
définie une seule fois pour toutes les instances. Il
est possible de l'appeler directement par la classe: |
Pas de sens |
final |
La variable est non modifiable dans l'instance courante. Elle doit donc être initialisée. Si on ajoute le mot clé "static", la variable est une constante de classe: static final int i=2; |
La méthode ne peut être surchargée dans une sous-classe |
La classe ne peut pas avoir de classes filles. L'extends est interdit. |
abstract |
Pas de sens Non applicable à une variable, uniquement les méthodes. |
La méthode est simplement définie dans une classe abstraite, doit être implémentée dans toutes les classes filles: abstract public String maMéthode(); |
La classe contient au moins une méthode abstraite. |
Lorsque l'on ne précise rien:
Une variable est dite 'package' ou 'friendly' , accessible par toutes les classes du même package ( classe courante, sous classes du même packages, non sous classes du même package ) mais par aucune classe d'un package diffèrent même par une classe fille.
Une méthode est dite 'package' ou 'friendly', la méthode est accessible par toutes les classes du même package ( classe courante, sous classes du même packages, non sous classes du même package ) mais par aucune classe d'un package diffèrent même par une classe fille.
La classe est dite 'package' ou 'friendly', elle peut être vue et dérivée par toute classe du même package mais par aucune classe d'un package différente. En clair, une classe fille doit se trouver dans le même package que sa mère si celle-ci est définie 'package', c'est à dire sans le mot clé 'public'.
Remarques:
Les spécificateurs public , private et protected sont appelés modificateurs d'accès.
Les modificateurs de type protected et private doivent être utilisées dès que le besoin s'en fait ressentir car cela apporte une plus grande intégrité de la classe et diminue le risque de bug. En outre, cela améliore le niveau de sécurité de l'application.
Une méthode ne peut pas être à la fois statique et abstraite.
Nous discuterons des arborescences logiques et physiques des classes Java.
Pour simplifier l'organisation des fichiers .class, il est possible de spécifier en début de source que les classes implémentées font partie d'un package, c'est-à-dire d'un ensemble de classes regroupées de façon fonctionnelle. Il suffit d'utiliser le mot clé package. Exemple dans un fichier .java:
package vehicule;
public class Voiture{
...
}
class Moto{
...
}
}
Dans un autre
fichier :
package vehicule;
public class Camion{
...
}
}
Il faut que les classes soient physiquement dans le même répertoire à l'exécution. De plus, les sous-packages sont possibles:
package vehicule.voiture;
public class VoitureEssence{
}
class
VoitureDiesel{
}
Physiquement les fichiers seront:
<...>/vehicule/voiture/VoitureEssence.class
<...>/vehicule/voiture/VoitureDiesel.class
Le nom complet d'une classe est donné par les noms des packages et sous packages de la classe + le nom de la classe, par exemple java.lang.Object. Le package le plus utilisé de Java est java.lang qui contient les classes de base ( dont la classe Object ) .
Noms de package normalisés SUN :
Les noms de package ne prennent jamais de majuscule et tiennent dans un seul mot.
Les chemins doivent respecter cette norme: <nom de la société>.<nom du projet>.<sous-partie du projet (facultatif)>....
Par exemple le package mycompany.tutorial.exemples contiendra la classe mycompany.tutorial.exemples.Voiture.
La plupart du temps, une classe utilise des objets d'une autre classe. Dès lors, il y a plusieurs cas:
Les classes utilisées sont dans le même package que la classe courante. Par exemple, une classe mycompany.tutorial.Classe1 peut utiliser la classe mycompany.tutorial.Classe2 mais pas la classe mycompany.tutorial.exemples.Classe3 qui se trouve à un niveau de package plus bas.
Les classes utilisées sont dans le package de base du langage : java.lang qui contient par exemple les objets Object, Integer, String.... Ces objets peuvent être utilisés immédiatement car le package est importé implicitement.
Les classes utilisées se trouvent dans un package différent. Dans ce cas, il faut alors explicitement déclarer que l'on va utiliser une classe en l'important.
Le mot clé est import. En début de source, avant la définition de la classe et après la déclaration du package, il faut importer les classes que l'on va utiliser:
package toto;
import
java.util.*; //
importe toutes les classes du package java.util
import
vehicule.Moto; //importe la classe Moto seule
class titi{ ...
}
Dès lors, la classe accède aux méthodes et variables de ces classes importées.
Remarques
Le package java.lang est toujours importé implicitement. Il est donc inutile de faire un "import java.lang.*"
Il n'est pas nécessaire d'importer les autres classes du package car elles le sont automatiquement.
Nous pouvons utiliser le caractère joker ' * ' pour importer toutes les classes d'un package mais attention! les classes plus basses dans l'arborescence ne seront pas importées.
Le fait de programmer en langage Objet conduit à la gestion de nombreux fichiers. En général, à une source .java correspond une classe mais il est possible - bien que dangereux - de définir plusieurs classes dans une même source à condition de n'en déclarer qu'une seule de type public ( les autres sont des classes dites outer ). En effet, si une classe est déclarée publique, elle doit figurer dans un fichier portant son nom et un fichier ne peut avoir qu'un nom. Il est également possible de définir une classe à l'intérieur d'une autre classe ( inner class ). Dans ce cas,après compilation, nous obtiendrons plusieurs fichiers .class
Étant donné que les contraintes hardware actuelles se portent davantage sur la taille des paquets transmis via un réseau que sur la vitesse des processeurs, les packages sont souvent compressés ( à la façon des .zip ) et forment des fichiers .jar qui sont décompressés à la volée à l'utilisation des classes.
Le package vehicule.* correspondant à toutes les classes situées récursivement dans ce répertoire peut être compressé en un fichier vehicule.jar. Pour compresser un répertoire dir recursivement en un fichier monjar.jar, lancez la commande :
jar cvf monjar.jar dir
Pour le décompresser :
jar xvf monjar.jar
La JVM Java "voit" un jar comme un répertoire et non comme un fichier. Nous trouvant dans un répertoire contenant un fichier tutorial.jar qui contient la classe mycompany.tutorial.Classe1.class , nous ferons :
java mycompany.tutorial.Classe1
Nous avons vu que les classes Java de différents packages parvenaient à se "connaître" entre elles grâce à la commande import. Cependant du point de vue de l'environnement système, les classes et les jars se trouvent dans des emplacements physiques pouvant être différents de l'emplacement courant. Dans les sources Java, les imports sont faits sur des classes se trouvant dans des emplacements "logiques"; il faut en plus indiquer au système l'emplacement de ces classes. C'est pourquoi il faut que l'environnement au moment du lancement du programme ait la variable d'environnement CLASSPATH correctement définie. La variable CLASSPATH contiendra les chemins des classes importées dans les programmes.
Sous UNIX, utiliser la commande :
export CLASSPATH=[nom des répertoires] : [nom des fichiers.jar] : $CLASSPATH
Exemple:
export CLASSPATH=.:/tmp/tutorial/exemples:/tmp/tutorial.jar:$CLASSPATH
Attention, les séparateurs sont des ':'
Sous Microsoft DOS, utiliser la commande
set CLASSPATH=[nom des répertoires] ; [nom des fichiers.jar] ; %CLASSPATH%
Attention, les séparateurs sont des ';'
Pour permettre l'import des classes d'un répertoire, il faut donner le nom du répertoire. Nous pourrons alors importer toutes les classes de ce répertoire et de ses sous-répertoires. Cependant, pour permettre l'import des classes d'un jar, il faut poser le chemin complet du jar et pas seulement le nom du répertoire dans lequel il se trouve. Il faut veiller à toujours avoir le répertoire courant '.' dans son CLASSPATH. En lançant un programme, le CLASSPATH doit au moins contenir le jar contenant le système Java de base ($JAVA_HOME/jre/lib/rt.jar en général). Il peut être utile d'avoir également $JAVA_HOME/jre/lib/tools.jar.
Remarque: Sur la plupart des Unices ( Linux, AIX, Solaris...), le programme 'java' est un shell qui inclus lui-même '.' et 'rt.jar' dans le CLASSPATH.
Lorsque le projet devient conséquent, il faut recourir à une documentation exhaustive et tenue à jour des classes, ce qui pourrait sembler impossible sans l'outil du JDK javadoc. Cet outil permet de générer une documentation complète et très aboutie en HTML du projet complet en quelques secondes. Il sera nécessaire de normaliser certaines partie du code (commentaires) pour obtenir des commentaires additionnels dans la documentation automatique. Pour les commentaires de méthodes, il suffit de procéder comme il suit:
/**
*
Ligne 1
* Ligne 2
**/
Cf. la documentation SUN pour plus de détails sur javadoc.
Écrire la classe mycompany.tutorial.TestSpecif possédant:
3 variables de type entier primitif :
- La variable i1 accessible par toutes les autres classes et valant 10.
- La variable i2 accessible que par la classe TestSpecif elle-même et valant 20
- La variable i3 ne pouvant être accédée que dans le package ou les classes filles et valant 30.
3 méthodes :
- La méthode m1() statique et qui ne peut être surchargée mais qui est utilisable par toutes les classes. Cette méthode affiche i1 sur la sortie standard.
- La méthode m2() qui n'est utilisable que dans TestSpecif et qui ajoute 10 à i2
- La méthode m3() qui est abstraite et renvoie void.
1- Compilez puis corrigez le message que vous devez obtenir, l'expliquer.
2- Écrire une méthode getI2() qui renvoie la valeur de i2. Cette méthode sera publique. Comment appelle-t-on ce type de méthode?
3- Écrire une méthode setI2(int) qui fixe la valeur de i2. Cette méthode sera publique. Comment appelle-t-on ce type de méthode?
Écrire la classe mycompany.tutorial.TestSpecifFille, dérivée de la classe TestSpecif.
1- Cette classe ne doit pas pouvoir être dérivée. Comment procéder?
2- Tenter de surcharger m1(). Que se passe-t-il? Pourquoi?
3- Écrire deux méthodes :
- m4() qui ajoute 10 à la variable i2 de TestSpécif. Pourquoi y a-t-il une erreur de compilation? Essayer avec i3. Est-ce mieux? pourquoi?
- Implémenter m3() qui était une méthode abstraite et qui doit afficher i3 sur la sortie standard.
Ajouter une méthode main() dans la classe TestSpecifFille qui affiche à l'écran les variables i1, i2 (avec getI2() ) et i3.
Attention la méthode main() a quelques particularités. Bien réfléchir aux impacts...
Dans cet exemple, les classes seront vides et définies comme ci-après:
class X{
}
1- Créer le package mycompany.tutorial.exemples1 qui contient les classes mycompany.tutorial.exemples1.Classe1 et mycompany.tutorial.exemples1.Classe2
2- Créer le package mycompany.tutorial.exemples2 qui contient la classe mycompany.tutorial.exemples2.Classe1
3- En admettant que les sources de ces classes se trouvent dans un répertoire quelconque et que l'on lance javac *.java, où se trouvent les classes compilées?
Lancer l'une de ces classes. Que se passe-t-il et pourquoi? Comment faut-il organiser les sources et les classes pour faciliter le développement puis permettre à l'application de fonctionner?
4- Dans la classe mycompany.tutorial.exemples1.Classe1 , implémenter une méthode main() qui affiche "Test". Comment lancez-vous cette classe?
5- Compresser l'arborescence mycompany en tutorial.jar par la commande jar. Puis consultez le contenu à l'aide d'un utilitaire de compression comme gzip ou winzip, que constatez vous ? Quelle est l'avantage de cette méthode ?