 |
2. CLASSES ET HERITAGE |
Comme en C++, il existe en Java deux types de variables, celles qui font référence
à des types primitifs (int, char, double...) et
celles qui représentent des objets. La sémantique concernant les variables de type
primitif en Java est identique à celle du C++. En revanche, les variables objets ont une
signification bien différente.
Il est dit qu'en Java il n'existe pas de pointeurs et que toutes les variables objets sont des
références sur des objets. Pour des personnes programmant en C++, le terme
"référence" a une signification bien précise qui le
différencie du terme "pointeur". En Java, le terme
"référence" correspond en fait au terme "pointeur" du C++,
à la seule différence qu'il est impossible pour le programmeur de manipuler
explicitement l'adresse encapsulée dans le pointeur (i.e. impossible d'afficher l'adresse ou
d'effectuer une opération arithmétique). Considérons l'exemple suivant qui
déclare une variable s de la classe String.
String s;
La variable est initialisée implicitement à null, indiquant qu'elle
ne référence aucun objet. A ce moment donc, aucun objet de la classe
String n'est créé.
La création d'un objet doit être demandée explicitement par le programmeur,
grâce au mot-clé new.
s = new String("hello !");
Comme en C++, new permet de passer des paramètres au constructeur de la
classe String. L'exemple suivant montre également que les variables en Java
sont des pointeurs.
void traduire(String t) { t=new String("Bonjour !"); }
...
String s = new String("Hello !");
traduire(s);
Les arguments des méthodes sont toujours passés par copie, ainsi le pointeur
s (ou la référence s) est copiée, et c'est sa copie
locale t qui référence la chaîne "Bonjour
!". La variable s reste donc inchangée après l'appel
à la méthode traduire.
| |
| Accès aux propriétés d'un objet |
| |
Toutes les variables objets étant des pointeurs en Java, il n'est plus nécessaire,
contrairement à C++, de faire la différence entre -> et
. pour appeler une méthode. En Java, seul le symbole . est
utilisé pour appeler une méthode. L'exemple suivant appelle la méthode
length de l'objet s.
int taille = s.length();
Toutes les variables étant finalement allouées dynamiquement, il serait fastidieux
d'effectuer manuellement toutes les libérations de mémoire. Un mécanisme
automatique, le ramasse-miettes (ou garbage collector), se charge de libérer
les objets une fois qu'ils ne sont plus référencés par aucune variable.
L'inconvénient ou l'avantage, selon les personnes, de cet automatisme est que le programmeur
n'a aucune idée du moment où les objets sont libérés et donc de l'appel
au destructeur. En effet, le ramasse-miettes n'est pas obligé de libérer la
mémoire tout de suite, il peut très bien attendre d'avoir besoin de ressources pour
effectuer un nettoyage.
La syntaxe pour définir une classe en Java est très proche de celle
employée en C++. Le plus simple pour la présenter est tout simplement de montrer un
exemple.
class Personne {
// Attributs privés d'instance
protected int numero;
protected String nom;
protected String prenom;
// Attribut privé de classe
protected static int compteur = 0;
// Constante
public static final int MAX = 100;
// Méthodes publiques d'instance
public int getNumero() { return numero; }
public String getNom() { return nom; }
public String getPrenom() { return prenom; }
// Constructeur
public Personne(String n,String p) {
nom=n;
prenom=p;
numero=++compteur;
}
// Destructeur
protected void finalize() {}
// Méthodes publiques de classe
public static int getNbPersonne() { return compteur; }
public static void main(String args[]) { ... }
}
Les mots clés public et protected permettent respectivement de
déclarer une propriété publique ou privée. Comme en C++,
static permet de définir des propriétés de classe. La combinaison
static final permet de définir des constantes. Le constructeur porte le nom de
la classe, et bien entendu, il peut y en avoir plusieurs. Le destructeur lui porte le nom
finalize. Il est conseillé de le déclarer uniquement s'il est
nécessaire de l'employer, ce qui est très rare du fait que la libération de la
mémoire est gérée automatiquement par le ramasse-miettes.
Lors de l'exécution d'un fichier, la JVM appelle la méthode main de
la classe publique du fichier. Si cette méthode n'existe pas, l'exécution
échoue. En paramètre, la méthode main reçoit un tableau de
chaînes de caractères qui correspondent aux arguments de la ligne de commande, de
manière similaire à C++ (cf. l'exemple de la classe Personne).
Avec Java, une classe définie sans super-classe hérite systématiquement de
la classe Object qui se trouve ainsi au sommet de la hiérarchie
d'héritage. Toute classe en Java hérite, directement ou indirectement, de cette
classe Object. Nous verrons l'intérêt de cette classe au chapitre
suivant.
| |
| Héritage simple et méthodes virtuelles |
| |
Java n'autorise que l'héritage simple grâce au mot clé extends.
L'héritage multiple n'est pas autorisé, à cause des nombreuses
ambiguïtés qu'il soulève. Pour permettre néanmoins cette
modélisation, la notion d'interface a été introduite, nous la
présentons dans le dernier chapitre. Considérons l'exemple suivant d'héritage
où la classe Cercle hérite de la classe Forme.
class Forme {
protected float x;
protected float y;
public Forme(float a,float b) {
x=a;
y=b;
}
public float aire() { return 0.0; }
}
class Cercle extends Forme {
protected float rayon;
protected static final float PI = 3.1415;
public Cercle(float a,float b,float r) {
super(a,b);
rayon=r;
}
public float aire() { return (PI*rayon*rayon); }
}
Contrairement au C++, les méthodes sont virtuelles par défaut en Java. Dans
l'exemple, la méthode aire est donc virtuelle et redéfinie dans la
classe Cercle.
Le mot-clé super représente la super-classe de la classe dans laquelle
on se trouve (il n'y en a qu'une puisqu'il n'y a pas d'héritage multiple). Ainsi, dans
l'exemple avec la classe Forme, le constructeur Cercle appelle le
constructeur de la classe Forme grâce à super(...). Il est
également possible pendant la redéfinition d'une méthode d'appeler la version de la
super-classe, comme le montre l'exemple suivant.
class Personne {
protected String nom;
protected String prenom;
...
public String getInfo() { return (nom+" "+prenom); }
}
class Employe extends Personne {
protected String poste;
...
public String getInfo() { return (super.getInfo()+" "+poste); }
}
Le mot-clé final permet de rendre une méthode finale, i.e. non
virtuelle, elle ne peut donc pas être redéfinie. Il est conseillé de rendre les
accesseurs (i.e. les méthodes get... et set... qui permettent de
manipuler les attributs) finaux, l'appel à une méthode virtuelle
étant une opération très coûteuse en comparaison avec l'appel à
une méthode finale. Ainsi, dans l'exemple de la section précédente, il faut
plutôt déclarer:
public final int getNumero() { return numero; }
public final String getNom() { return nom; }
public final String getPrenom() { return prenom; }
Il est possible de définir des méthodes abstraites, i.e. sans corps, grâce
au mot-clé abstract. L'exemple de la classe Forme est
modifié pour proposer la méthode aire abstraite.
abstract class Forme {
protected float x;
protected float y;
public Forme(float a,float b) {
x=a;
y=b;
}
public abstract float aire();
}
class Cercle extends Forme {
protected float rayon;
protected static final float PI = 3.1415;
public Cercle(float a,float b,float r) {
super(a,b);
rayon=r;
}
public float aire() { return (PI*rayon*rayon); }
}
Dans ce cas, la classe Forme est abstraite également, ce qui signifie
qu'elle ne peut pas être instanciée, aucun objet purement de cette classe ne peut
être créé.
| |
| |
| Copyright (c) 1999-2016 - Bruno Bachelet - bruno@nawouak.net -
http://www.nawouak.net |
| La permission est accordée de copier, distribuer et/ou modifier ce document
sous les termes de la licence GNU Free Documentation License, Version 1.1 ou toute
version ultérieure publiée par la fondation Free Software Foundation. Voir
cette licence pour plus de détails (http://www.gnu.org). |
|
|