Variables et méthodes de classe
Dans le modèle objet, toutes les variables et méthodes sont liées à un objet. C'est-à-dire que pour accéder à une donnée ou déclencher un traitement, il faut d'abord instancier une classe.
Cette nécessité de créer d'abord un objet est difficile à respecter dans certains cas particuliers :
- Déclenchement du point d'entrée du programme : la méthode
main. Comment envoyer un message sans avoir à créer d'objets ? - Les flots d'entrée / sortie standard référencés par les variables constantes
out,in,errdoivent être définis d'une manière unique avant le début de l'exécution. Où définir ces variables et comment les positionner avant l'appel au point d'entrée du programme ? - Les opérations mathématiques (
abs(),sin()...) s'appliquent, en Java, à des types de base. Comment rendre disponibles ces opérations qui ne s'appliquent pas à un objet ?
La construction classe va servir à contenir les données et traitements qui ne sont pas liés à des objets. Une classe n'est plus seulement la description d'une encapsulation mais devient utilisable à l'exécution.
Par défaut, une méthode ou une variable est attachée à un objet. Le mot-clé
static permet d'attacher une méthode ou une variable à une classe. Comme pour
un objet, c'est la notation pointée qui est utilisée pour accéder à ces nouveaux
membres. C'est un message envoyé à la classe et non à une instance de cette
classe, par exemple : java.lang.System.out, java.lang.Math.abs().
Variable de classe
Une variable de classe est partagée par toutes les instances de la classe. Sa modification affecte toutes les instances. Une variable de classe existe avant toute instanciation de la classe.
static [final] [public | private] type nom;
- Même si une variable de classe n'est pas stockée dans la zone mémoire de l'objet, elle fait partie de l'ensemble des attributs de chaque instance de cette classe.
- L'initialisation d'une variable de classe s'effectue au chargement de la classe. Elle peut se faire dans un "bloc statique" déclaré à l'intérieur de la classe. La machine virtuelle exécute le bloc statique à l'initialisation de la classe, par exemple pour initialiser une variable de classe qui contient un tableau.
class Exemple {
static final private Porte[] portes = new Porte[10];
static private double[] mesReels = new double[20];
// ...
static {
for (int i = 0; i < portes.length; i++)
portes[i] = new PorteCharniere();
for (int i = 0; i < mesReels.length; i++)
mesReels[i] = Math.random();
}
}
Méthode de classe
static [public | private] type methodeDeClasse([liste de paramètres]) {
// code
}
- Le code d'une méthode de classe a accès aux variables de classe et aux méthodes de classe définies dans la classe.
- Par contre, la référence
thisn'est pas définie (aucune instance n'est concernée). Il n'est donc pas possible d'utiliser directement les variables d'instance et les méthodes d'instance. L'utilisation doit se faire à travers une instance de la classe, soit passée par paramètre, soit instanciée par le code de la méthode. - Une méthode de classe peut être surchargée (mais pas redéfinie : une méthode de classe de même prototype dans une classe dérivée la masque).
Les méthodes de classe s'emploient couramment pour fabriquer des instances ou contenir les traitements ne correspondant à aucune encapsulation.
Pour simplifier l'écriture de l'appel à une variable de classe ou une méthode de
classe d'une autre classe, il est possible d'utiliser la déclaration
import static au début du fichier. Par exemple import static java.lang.Math.*;
permet d'écrire directement PI dans le code.
Statut de la classe en Java
Avec l'introduction des variables de classe et des méthodes de classe, la classe n'est plus seulement une construction syntaxique. Elle peut être assimilée à la notion de module mais aussi à la notion d'objet.
Dans le modèle objet, les objets sont les seules entités d'exécution. Si les classes sont utilisables à l'exécution (envoi de messages), ce sont forcément des objets. La classe est donc la mise en œuvre d'une encapsulation particulière décrivant le service commun aux classes. Ce service fournit les informations sur les déclarations / définitions contenues dans une classe : variables, portée, méthodes, constructeurs, liste de paramètres...
C'est le choix du langage Java :
- la description de l'encapsulation particulière
classest définie dans la classejava.lang.Class. Chaque classe Java est représentée à l'exécution par une instance dejava.lang.Class; - à l'exécution, une classe chargée est représentée d'une manière unique (pour
un chargeur de classes donné) par cette instance, accessible avec la notation
NomClasse.class:System.out.println(tec.EtatPassager.class); - il est possible de définir des variables du type
Class; - quelques opérations définies dans
java.lang.Class:forName(String)permet de récupérer l'instance deClasscorrespondant au nom complet en paramètre ;newInstance()permet d'instancier la classe qui reçoit ce message (en utilisant le constructeur sans paramètre) ; cette méthode est dépréciée depuis Java 9 au profit degetDeclaredConstructor().newInstance();getFields(),getMethods(),getSuperclass(),getConstructors()fournissent des informations sur la définition d'une classe.
Ces opérations autorisent l'introspection des classes Java (voir les classes du
paquetage java.lang.reflect). L'introspection est une technique de réflexion
qui permet à un programme d'examiner sa propre structure pendant l'exécution ;
la modification de cette structure relève de l'intercession, que Java ne permet
que de façon limitée (par exemple modifier la valeur d'un attribut avec
Field.set).
Dans les classes de test, pour éviter d'oublier de compléter la méthode
lancer(), il suffit par introspection de récupérer à l'exécution toutes les
méthodes dont le nom commence par la sous-chaîne test et de les exécuter.
public void runTest() throws Exception {
Class<?> c = this.getClass();
java.lang.reflect.Method[] mesMethodes = c.getMethods();
int nbTest = 0;
for (int i = 0; i < mesMethodes.length; i++) {
java.lang.reflect.Method m = mesMethodes[i];
if (m.getName().startsWith("test")) {
System.out.print(".");
m.invoke(this);
nbTest++;
}
}
System.out.println("(" + nbTest + "):OK: " + getClass().getName());
}