Classes
Définition : une classe est un moule qui décrit le quoi (la spécification des messages qui peuvent être envoyés à l'objet) et le comment (la manière de réaliser cette spécification).
Du point de vue de l'instanciation, les classes se répartissent en trois formes :
- Classe concrète : une classe instanciable.
- Classe abstraite : la réalisation est incomplète, au moins une des
méthodes d'instance n'a pas de mise en œuvre (pas de corps défini). En Java,
une classe peut aussi être déclarée
abstractsans contenir de méthode abstraite, uniquement pour interdire son instanciation. - Classe abstraite pure ou interface : aucune réalisation n'est définie, elle contient les déclarations des méthodes d'instance.
Ce chapitre est centré sur la construction des classes concrètes.
Classe Java
La syntaxe du langage Java différencie la construction d'une classe contenant une réalisation et d'une classe ne contenant pas de réalisation.
- Construction avec réalisation :
- classe concrète avec le mot-clé seul
class; - classe abstraite en ajoutant le mot-clé
abstractau mot-cléclass.
- classe concrète avec le mot-clé seul
- Construction sans réalisation : classe abstraite pure avec le mot-clé
interface. Cette construction ne contient ni variables d'instance, ni constructeurs (seulement des constantes de classe ; depuis Java 8, une interface peut aussi contenir des méthodesstaticet des méthodesdefaultavec du code, voir Hiérarchie de classes).
Une classe / interface Java correspond à un fichier .class obtenu après
compilation (commande javac) d'un fichier source. Ce fichier (en bytecode
ou code intermédiaire) contient le code à exécuter mais aussi les informations
sur la définition de la classe, par exemple le type des paramètres d'une
méthode.
Le contenu des fichiers .class est utilisé par le compilateur (pour les
informations de type) et par la machine virtuelle Java (JVM) (exécution du code
intermédiaire).
Paquetage
Les paquetages permettent de regrouper les classes / interfaces par thème. Le
système de paquetage est hiérarchique avec la notion de sous-paquetage. Le
caractère . sert de séparateur. Les paquetages commençant par java ou
javax font partie de la distribution standard (JRE et JDK).
Un paquetage correspond à un espace de noms : le nom d'une classe / interface
est unique dans un paquetage. Le nom complet d'une classe / interface est
constitué de la hiérarchie de paquetage puis du nom de la classe / interface,
par exemple java.lang.String. L'inclusion d'une classe / interface dans un
paquetage est précisée par l'instruction package nom;, qui doit être la
première instruction du fichier source. Sans cette instruction, les classes sont
incluses dans un paquetage unique sans nom (paquetage anonyme ou paquetage par
défaut). Pour désigner une classe / interface à l'extérieur de son paquetage, il
est nécessaire d'utiliser son nom complet :
matos.onde.DentBleu maDent = new matos.onde.DentBleu();. Dans l'écriture d'un
fichier source, il est possible de raccourcir le nom complet d'une classe à son
nom, en utilisant le mot-clé import. Au début du fichier source,
l'instruction import matos.onde.DentBleu; permet de désigner la classe
uniquement par DentBleu. Le compilateur lui attribue son nom complet.
Organisation physique des paquetages
L'organisation physique des fichiers .class correspond à la hiérarchie de
paquetage. Le fichier String.class se trouve dans l'arborescence de
répertoires java/lang. Lorsque le compilateur ou la machine virtuelle ont
besoin de la définition d'une classe, ils utilisent son nom complet comme chemin
de recherche du fichier .class. Par défaut, la recherche s'effectue à partir
du répertoire courant (celui du lancement de la commande).
L'option -cp et la variable d'environnement CLASSPATH permettent d'indiquer
les répertoires (ou archives .jar) à analyser pour trouver ce chemin.
Classe concrète
La construction Java class contient trois catégories de membres : les
variables, les méthodes et les constructeurs.
Portée des membres d'une classe
Les modificateurs de portée précisent la portée d'utilisation d'un membre d'une classe. Ils se placent devant chaque définition :
private: accessible uniquement par la classe ;public: accessible partout ;- au paquetage (pas de mot-clé) : accessible par les classes du même paquetage.
Un quatrième modificateur, protected, donne accès aux classes du même
paquetage et aux classes dérivées ; il est présenté avec l'héritage.
Une classe (de premier niveau) possède deux portées. Sans mot-clé, sa portée est
restreinte à son paquetage. Avec le mot-clé public, elle est accessible à
l'extérieur de son paquetage : public class DentBleu.
Définition d'une classe concrète
Définition d'une classe concrète Java et de ses membres :
[public] class NomClasse {
// Variables d'instance / attributs
[final] [public | private] type nom [= expression];
// Méthodes d'instance
[public | private] typeRetour methodeInstance([liste de paramètres]) {
// code
}
// Constructeurs
[public | private] NomClasse([liste de paramètres]) {
// code
}
}
Variable d'instance
- C'est une variable propre à chaque instance. Sa valeur est stockée dans la zone mémoire de l'instance.
- Son initialisation s'effectue soit à sa déclaration, soit dans le code d'un
constructeur. La machine virtuelle Java initialise systématiquement les
variables d'instance avec une valeur par défaut dépendant du type
(référence /
null,int/0,boolean/false). - Le mot-clé
finaldevant la définition d'une variable correspond à une variable constante. Elle doit être initialisée exactement une fois : à sa déclaration ou dans chaque constructeur.
public class Telecommande {
private int nbPortes;
private final PorteCharniere[] mesPortes;
private final int NB_MAX;
public Telecommande(int capacite) {
NB_MAX = capacite;
nbPortes = 0;
mesPortes = new PorteCharniere[NB_MAX];
}
public void positionner(PorteCharniere p) {
if (NB_MAX <= nbPortes)
return;
mesPortes[nbPortes] = p;
nbPortes++;
}
public boolean estRempli() {
return NB_MAX <= nbPortes;
}
public void activer(int numero) {
mesPortes[numero].ouvrir();
}
}
Méthode d'instance
Une méthode d'instance s'applique à une instance. Il existe deux catégories de méthodes :
- méthode d'accès, ou accesseur : elle donne des informations sur l'état des attributs ;
- méthode d'altération, ou modificateur : elle modifie l'état des attributs.
Le mécanisme de surcharge autorise plusieurs méthodes à porter le même nom. La
distinction se fait, à la compilation, en fonction de la liste des paramètres
d'appel (nombre, type et ordre) ; le type de retour n'intervient pas. Par
exemple, la méthode println de la classe java.io.PrintStream.
Le mot-clé this désigne / référence l'instance qui vient de recevoir le
message. Il est utilisé dans le code :
- soit pour passer l'objet en paramètre d'une méthode (ou d'un constructeur) ;
- soit pour désigner un attribut en cas d'ambiguïté.
public class Telecommande {
private int nbPortes;
private final PorteCharniere[] mesPortes;
public Telecommande(int nbPortes) {
this.nbPortes = 0;
this.mesPortes = new PorteCharniere[nbPortes];
}
public void positionner(PorteCharniere p) {
if (this.estRempli())
return;
this.mesPortes[nbPortes] = p;
this.nbPortes++;
}
public boolean estRempli() {
return this.mesPortes.length <= this.nbPortes;
}
public void activer(int numero) {
this.mesPortes[numero].ouvrir();
}
public void desactiver(int numero) {
this.mesPortes[numero].fermer();
}
public void desactiver() {
for (int i = 0; i < this.nbPortes; i++)
if (!this.mesPortes[i].estFerme())
this.desactiver(i);
}
}
// Quelles références this faut-il conserver ?
Exemple d'appel avec surcharge résolue à la compilation :
tele.desactiver(1);
tele.desactiver();
Constructeur
- Un constructeur n'est pas une méthode. Il est uniquement appelé à l'instanciation. Son code assure la bonne initialisation des variables d'instance.
- Le constructeur prend le nom de la classe. Il n'a pas de type de retour.
- L'instance existe au moment de l'appel du constructeur. Le code du
constructeur peut utiliser les méthodes d'instance et la référence
this. - Si une classe ne contient aucun constructeur, le compilateur Java ajoute un constructeur sans paramètre avec un corps vide (désigné par le terme constructeur par défaut), de même portée que la classe.
- La surcharge des constructeurs est autorisée.
- Avec la définition de plusieurs constructeurs, il est nécessaire d'avoir un
mécanisme de factorisation du code d'initialisation. Un constructeur peut
déclencher le code d'un autre constructeur par l'instruction (qui utilise le
mécanisme de surcharge)
this(par1, par2, par3);. Cette instruction doit être la première instruction du constructeur.
Lien "a-un" entre classes
Le lien "a-un" correspond à la relation d'utilisation la plus basique : la réalisation d'une encapsulation nécessite l'appel aux méthodes d'une autre encapsulation.
Relation d'utilisation dans la réalisation
La classe PorteCharniere possède un lien "a-un" avec la classe DentBleu. Une
instance de PorteCharniere contient dans ses attributs la référence à une
instance de DentBleu.
Dans notre cas de réalisation, l'instance de DentBleu ne peut pas être
partagée et doit être utilisée dès l'instanciation de PorteCharniere.
Le lien "a-un" se traduit dans le code de la classe PorteCharniere par :
- la définition d'une variable d'instance du type
DentBleu; - l'instanciation de
DentBleudans le constructeur et l'affectation à la variable d'instance.
Factoriser du code
Pour éviter de dupliquer le code, la construction de la classe
PorteVerrouCharniere peut se faire en utilisant un lien "a-un" avec la classe
PorteCharniere. L'instanciation de la classe PorteVerrouCharniere doit alors
instancier la classe PorteCharniere.