UKOnline

Classe, variable d'instance et constructeur

En Java, tout se passe donc dans des classes. Celles qu'on a déjà écrites ne contenaient qu'une méthode main et servent à définir un programme. On va maintenant voir comment définir des classes à partir desquelles on peut créer des objets. Pour cela, on va devoir définir des variables d'instances pour les attributs et des méthodes pour les fonctionnalités.

Classe

On va partir d'un exemple simple pour découvrir ensemble comment définir une nouvelle classe et l'utiliser. Pour cela, on utilise le mot réservé class suivi d'un nom qui doit respecter la règle de formation des identificateurs, vue au premier chapitre.

Supposons qu'on souhaite écrire une classe qui représente un dé (die en anglais). Le code suivant montre la déclaration de classe, qu'on a appelée Die. Tout ce qui se trouve entre les accolades est appelé corps de la classe; c'est à cet endroit qu'on va mettre les définitions des variables d'instances et des méthodes.

Remarquez qu'on a utilisé le mot réservé public devant class, ne vous en occupez pas pour le moment, on va revenir dessus très vite. Pour le moment, on retient donc que pour déclarer une nouvelle classe, on écrit public class suivi d'un identificateur qui est le nom de la classe. La figure 4 montre les différents éléments d'une définition de classe.

Définition de classe
Définition d'une classe : nom et corps de classe.

Variable d'instance

On peut déclarer des variables d'instance dans le corps de la classe. Elles vont permettre de stocker des informations sur l'objet en représentent ses attributs.

Continuons à écrire notre classe représentant un dé. On doit se poser la question suivante : « Quels sont les attributs d'un dé ? » ou « Qu'est-ce-qui caractérise un dé ? ». Bien entendu, de nombreux attributs sont possibles : couleur, dimension, nombre de faces, matériau utilisé, poids, volume...

Pour notre exemple, un dé sera caractérisé par son nombre total de faces, ainsi que le nombre indiqué par la face qui est « visible » du dé (celle orientée vers le haut). Voici les deux déclarations qu'il faut ajouter :

On déclare donc les variables d'instance comme n'importe quelle autre variable, c'est-à-dire en spécifiant un type et un nom. La seule différence est qu'on a ajouté le mot réservé private avant le type; on verra l'utilité de ce mot réservé à la fin de cette section.

Les variables d'instance ont une portée qui s'étend sur tout le corps de la classe. Rappelez-vous le chapitre 3, la portée d'une variable définit les endroits du code source où elle est accessible, c'est-à-dire qu'on peut accéder à son contenu et le modifier. Les variables qu'on a utilisées jusqu'à présent étaient déclarées dans la méthode main et leur portée s'étendait depuis leur déclaration jusqu'à la fin du bloc la contenant; ce sont des variables locales qu'on verra en détail à la section suivante.

Instance

Pour rappel, un objet est une instance d'une classe. Chaque objet est créé à partir d'une classe et possède sa propre copie de toutes les variables d'instance, d'où leurs noms. L'exemple suivant crée deux instances de la classe Die :

Lors de l'exécution du programme, deux objets de type Die sont créés; on peut donc créer autant d'instances que l'on souhaite à partir d'une classe. La figure 5 montre les variables et objets créés en mémoire. Il y a deux variables de1 et de2 de type Die et deux objets Die. Des références vers ces objets sont stockées dans les variables. Vous voyez clairement que chaque objet possède sa propre copie de toutes les variables d'instances définies dans la classe.

Intances de Die
Deux instances de la classe Die.

Valeur par défaut

Dans la définition de la classe Die, on n'a pas initialisé les deux variables d'instance. Néanmoins, si vous vous rappelez du premier chapitre, une variable non initialisée ne contient pas de valeur et on ne peut pas l'utiliser dans des expressions, sans quoi on se retrouve avec une erreur d'exécution. En ce qui concerne les variables d'instance, on n'est pas obligé de les initialiser car si on ne le fait pas, elles le seront implicitement; en effet, elles reçoivent une valeur par défaut. La figure 6 donne les valeurs par défaut pour les variables de type primitifs.

Types Valeur
byte, short, int, long 0
float, double 0.0
char '\0'
boolean false
Valeurs par défaut des variables de types primitifs.

Constructeur

Lorsqu'on définit une classe, il faut également définir un constructeur. Celui-ci permet de définir comment l'objet doit être construit. C'est grâce à sa présence qu'il est possible d'utiliser le mot réservé new pour créer des instances d'une classe. Voici le constructeur de la classe Die :

La syntaxe à utiliser pour le constructeur est présentée à la figure 7. On commence par le mot réservé public suivi du nom de la classe et de deux parenthèses (). Le corps du constructeur se trouve entre accolades, et comporte toutes les instructions qui composent le constructeur.

Définition de constructeur
Définition du constructeur d'un classe.

Dans notre exemple, le constructeur se limite à deux instructions. Elles initialisent les variables d'instance. Pour rappel, les variables d'instance ont une portée qui s'étend à tout le corps de la classe, on peut dès lors les utiliser puisque le constructeur fait partie du corps de la classe.

Ce constructeur définit un dé à six faces et « lance le dé » pour déterminer la face visible initiale. Pour ce faire, on utilise la méthode de classe random de la classe Math qui renvoie un double compris entre 0 et 1 (non compris). On peut maintenant recréer deux instances de la classe Die :

L'opérateur new va exécuter le constructeur de la classe. La figure 8 montre une situation possible. La variable nbFaces vaut 6 pour les deux objets et en ce qui concerne la variable visibleFace, elle prend une valeur au hasard comprise entre 1 et 6.

Instances de Die
Deux instances de la classe Die.

Les instructions du constructeur sont exécutées lors de la création d'une nouvelle instance d'une classe, faite avec l'opérateur new. Il va typiquement initialiser les variables d'instances. Une fois le constructeur complètement exécuté, l'objet est dans son état initial : il est créé et on peut affecter une référence vers ce dernier à une variable.

Modificateur de visibilité

Il reste un point à éclaircir un point à propos des variables d'instance, c'est l'utilisation du mot réservé private utilisé dans leur déclaration. Il permet de modifier la visibilité des variables d'instance.

Les variables d'instance sont des membres de la classe. Dès lors qu'on possède une instance de la classe, on peut accéder aux variables d'instance avec l'opérateur d'accès vu précédemment (le point .). Il s'agit en fait de l'opérateur d'accès à un membre, grâce auquel on peut accéder à n'importe quel membre d'une classe à partir d'une variable contenant une référence vers une instance de la classe.

On peut donc écrire un programme qui tente d'afficher la valeur de la face supérieure d'un dé en utilisant l'opérateur point et en spécifiant le nom de la variable d'instance qui nous intéresse, à savoir visibleFace :

Ce programme ne va pas compiler. En effet, une tentative de compilation produit le message d'erreur suivant :

Visibility.java:6: visibleFace has private access in Die
		System.out.println ("La face visible du dé est " + myDie.visibleFace);
		                                                         ^
1 error

Cela signifie que la variable visibleFace n'est pas visible depuis cette méthode main se trouvant dans la classe Visibility. Le mot réservé private est un modificateur de visibilité qui permet de restreindre la visibilité de la variable d'instance à la classe dans laquelle elle est déclarée.

On ne pourra accéder aux variables d'instance de la classe Die que depuis du code écrit dans cette même classe. Il est important de faire la différence entre la portée et la visibilité d'une variable. La portée est une propriété que toutes les variables possèdent et qui indique où dans le code source du programme la variable existe et peut être utilisée en utilisant son nom. La visibilité indique où il est possible d'accéder à la variable en utilisant l'opérateur d'accès.

Les deux modificateurs de visibilité qu'on a déjà rencontré sont public et private. Le premier indique une visibilité universelle, c'est-à-dire que la variable est accessible de partout. Le second restreint l'accès au corps de la classe dans laquelle la variable est définie.

Si la méthode main produisant l'erreur avait été placée dans la classe Die, le programme aurait compilé et se serait exécuté sans problème.

On se pose maintenant une question légitime. Quelle visibilité choisir pour les variables d'instance ? Cette question est traitée plus tard, à la section 5.6. Sachez néanmoins que, la plupart du temps, les variables d'instance sont déclarées private. Les modificateurs de visibilité s'appliquent à tous les membres de la classe : variables d'instance, constructeurs et méthodes qu'on voit à la section suivante.

Construction de l'objet

L'opérateur new est utilisé pour créer une nouvelle instance d'une classe. La construction d'un nouvel objet se passe en deux étapes :

  1. création et initialisation des variables d'instances ;
  2. exécution du constructeur.

Les variables d'instances sont créées selon l'ordre dans lequel elles apparaissent dans le code source de la classe. On peut les initialiser directement lors de leur déclaration. On peut déclarer les variables d'instance où on veut dans le corps de la classe, en dehors de tout constructeur ou méthode. Voici une autre version de la classe Die, qui représente le même dé que celui définit précédemment. L'un des deux variables d'instance a été directement initialisée lors de la déclaration et l'autre l'a été via le constructeur :

Constructeur par défaut

Pour la classe Die, on pourrait donc initialiser directement toutes les variables d'instance et se passer de constructeur :

On n'est donc pas obligé de définir un constructeur dans une classe. Si on ne définit pas de constructeur explicitement, il faut savoir qu'il y a un constructeur par défaut ajouté par le compilateur. Ce constructeur implicite possède un corps vide. Donc, pour l'exemple précédent, le compilateur a ajouté le constructeur suivant :

Le constructeur par défaut n'est ajouté que lorsqu'aucun autre constructeur n'est présent explicitement dans la classe. Si vous définissez un constructeur, le compilateur n'ajoutera pas celui par défaut.