6. Chapitre 3. Animaux familiers et poissons - Classes Java▲
Les programmes Java sont constitués de classes qui représentent des objets du monde réel. Même si chaque personne écrit des programmes à sa façon, presque tout le monde est d'accord pour reconnaître que la meilleure façon de le faire est d'employer le style dit orienté objet. Cela signifie que les bons programmeurs commencent par décider quels objets doivent être inclus dans le programme et quelles classes Java vont les représenter. Ce n'est qu'une fois cette étape menée à bien qu'ils commencent à écrire du code Java.
6-A. Classes et Objets▲
Créons et discutons une classe nommée JeuVidéo. Cette classe peut avoir plusieurs méthodes, qui représentent les actions que les objets de cette classe peuvent effectuer : démarrer le jeu, l'arrêter, enregistrer le score, etc. Cette classe peut aussi posséder des attributs ou propriétés : prix, couleur de l'écran, nombre de télécommandes et autres.
En langage Java, cette classe pourrait ressembler à ceci :
class
JeuVidéo {
String couleur;
int
prix;
void
démarrer (
) {
}
void
arrêter (
) {
}
void
sauverScore
(
String nomJoueur, int
score) {
}
}
Notre classe JeuVidéo est sans doute similaire aux autres classes qui représentent des jeux vidéo - ceux-ci ont tous des écrans de taille et de couleur différentes, effectuent des actions similaires et coûtent de l'argent.
Nous pouvons être plus spécifiques et créer une autre classe Java, nommée GameBoyAdvance. Elle appartient aussi à la famille des jeux vidéo, mais possède certaines propriétés spécifiques au modèle GameBoy Advance, par exemple un type de cartouche.
class
GameBoyAdvance {
String typeCartouche;
int
largeurEcran;
void
démarrerJeu
(
) {
}
void
arrêterJeu
(
) {
}
}
Dans cet exemple, la classe GameBoyAdvance définit deux attributs - typeCartouche et largeurEcran - et deux méthodes - démarrerJeu et arrêterJeu. Mais, pour l'instant, ces méthodes ne peuvent effectuer aucune action, parce qu'il n'y a pas de code Java entre leurs accolades.
Après le mot classe, tu dois maintenant t'habituer à la nouvelle signification du mot objet. La phrase « créer une instance d'un objet » signifie créer une copie de cet objet dans la mémoire de l'ordinateur en respectant la définition de sa classe.
Un plan de fabrication de la GameBoy Advance correspond à une console réelle de la même façon qu'une classe Java correspond à l'une de ses instances en mémoire. Le processus de fabrication des consoles réelles d'après ce plan est similaire au processus de création d'instances d'objets GameBoyAdvance en Java.
La plupart du temps, un programme ne pourra utiliser une classe Java qu'après qu'une instance en ait été créée. De même, les fabricants créent des milliers de copies d'une console de jeu à partir d'une même description. Bien que ces copies représentent la même classe, leurs attributs peuvent avoir des valeurs différentes - certaines sont bleues alors que d'autres sont argentées, et ainsi de suite. En d'autres termes, un programme peut créer de multiples instances d'objets GameBoyAdvance.
6-B. Types de données▲
Les variables Java peuvent représenter les attributs d'une classe, les arguments d'une méthode ou être utilisées à l'intérieur d'une méthode pour stocker temporairement certaines données. Les variables doivent être déclarées avant de pouvoir être utilisées.
Te souviens-tu d'équations telles que y = x + 2 ? En Java, tu dois déclarer les variables x et y en précisant qu'elles appartiennent à un type de données numérique tel que int ou double :
int
x;
int
y;
Les deux lignes suivantes montrent comment tu peux affecter une valeur à ces variables. Si ton programme donne la valeur 5 à la variable x, la variable y sera égale à 7 :
x = 5;
y = x + 2;
En Java, tu peux aussi changer la valeur d'une variable d'une façon assez inhabituelle. Les deux lignes suivantes modifient la valeur de la variable y, qui passe de 5 à 6 :
int
y =
5
;
y++
;
Bien qu'il y ait deux signes plus, Java va tout de même incrémenter la valeur de la variable y de 1.
Après le fragment de code suivant, la valeur de la variable monScore est aussi 6 :
int
monScore =
5
;
monScore =
monScore +
1
;
On peut utiliser de la même manière la multiplication, la division et la soustraction. Regarde l'extrait de code suivant :
int
monScore =
10
;
monScore--
;
monScore =
monScore *
2
;
monScore =
monScore /
3
;
System.out.println
(
"Mon score est "
+
monScore);
Qu'imprime ce code ? Eclipse a une fonctionnalité bien pratique appelée page de testeur de code, qui permet de tester rapidement n'importe quel bout de code (comme le précédent) sans même créer une classe. Sélectionne le menu Fichier, Nouveau, Autre…, puis Java, Exécution/Débogage Java, Page de testeur de code et entre le mot Test comme nom de ta page.
Entre maintenant dans la page ces cinq lignes de code utilisant monScore, sélectionne-les et clique sur la petite flèche verte accompagnée d'un J dans la barre d'outils.
Pour voir le résultat des calculs, clique simplement sur l'onglet Console en bas de l'écran :
Mon score est 6
Dans cet exemple, l'argument de la méthode println() a été fabriqué en accolant deux morceaux - le texte « Mon score est » et la valeur de la variable monScore, c'est-à-dire 6. On appelle concaténation la création d'une chaîne de caractères (String) à partir de morceaux. Bien que monScore soit un nombre, Java est suffisamment malin pour convertir cette variable en String puis l'accoler au texte « Mon score est ».
Voyons d'autres façons de modifier les valeurs des variables :
monScore = monScore * 2; est équivalent à monScore *= 2;
monScore = monScore + 2; est équivalent à monScore += 2;
monScore = monScore - 2; est équivalent à monScore -= 2;
monScore = monScore / 2; est équivalent à monScore /= 2;
Il y a huit types de données simples, ou primaires en Java, et tu dois décider lequel utiliser en fonction du type et de la taille des données que tu as l'intention de stocker dans chaque variable :
- Quatre types de données pour les valeurs entières - byte, short, int et long.
- Deux types de données pour les valeurs décimales - float et double.
- Un type de données qui permet de stocker un caractère isolé - char.
- Un type de données logique nommé boolean qui autorise seulement deux valeurs : true (vrai) ou false (faux).
On peut affecter une valeur initiale à une variable lors de sa déclaration. On parle alors d'initialisation de la variable :
char
niveau =
'E'
;
int
chaises =
12
;
boolean
sonActif =
false
;
double
revenuNational =
23863494965745.78
;
float
prixJeu =
12.50
f;
long
totalVoitures =
4637283648392
l;
Dans les deux dernières lignes, f signifie float et l signifie long.
Si tu n'initialises pas les variables, Java le fera pour toi en donnant la valeur 0 à chaque variable numérique, la valeur false aux variables de type boolean et le code spécial '\u0000' aux variables de type char.
Il y a aussi le mot-clé spécial final ; s'il est utilisé dans la déclaration d'une variable, on ne peut affecter de valeur à cette variable qu'une fois, cette valeur ne pouvant plus être modifiée par la suite. Dans certains langages, les variables invariantes sont appelées constantes. En Java les noms des variables invariantes sont généralement écrits en lettres majuscules :
final
String CAPITALE_ETAT =
"Washington"
;
Outre les types de données primaires, on peut aussi utiliser les classes Java pour déclarer des variables. À chaque type de données primaire correspond une classe enveloppe, par exemple Integer, Double, Boolean, etc. Ces classes possèdent des méthodes utiles pour convertir les données d'un type à un autre.
Alors que le type de données char est utilisé pour stocker un caractère isolé, Java a aussi une classe String permettant de manipuler un texte plus long, par exemple :
String nom =
"Dupont"
;
En Java, les noms de variables ne peuvent pas commencer par un chiffre ni contenir d'espaces.
Un bit est le plus petit bout de donnée que l'on puisse stocker en mémoire. Il contient soit 1, soit 0. Un octet (byte) est composé de huit bits. En Java, un char occupe deux octets en mémoire. En Java, un int ou un float occupe quatre octets. Les variables de type long ou double utilisent huit octets. Les types de données numériques qui utilisent plus d'octets peuvent stocker de plus grands nombres. 1 kilooctet (KO) correspond à 1024 octets. 1 mégaoctet (MO) correspond à 1024 kilooctets. 1 gigaoctet (GO) correspond à 1024 mégaoctets.
6-C. Création d'un animal familier▲
Nous allons concevoir et créer une classe AnimalFamilier. Tout d'abord, nous devons décider quelles actions notre animal familier sera capable d'effectuer. Que dirais-tu de manger, dormir et dire ? Nous allons programmer ces actions dans les méthodes de la classe AnimalFamilier. Nous allons aussi donner à notre animal les attributs suivants : âge, taille, poids et couleur.
Commençons par créer une nouvelle classe Java nommée AnimalFamilier dans Mon Premier Projet comme décrit dans le chapitre 2, mais sans cocher la case de création de la méthode main().
Ton écran devrait ressembler à ceci :
Nous sommes maintenant prêts à déclarer les attributs et les méthodes de la classe AnimalFamilier. Le corps des méthodes et des classes Java est délimité par des accolades. À chaque accolade ouvrante doit correspondre une accolade fermante :
class
AnimalFamilier {
}
Pour déclarer les variables constituant les attributs de la classe, nous devons choisir leur type. Prenons par exemple le type int pour l'âge, float pour la taille et le poids et String pour la couleur.
class
AnimalFamilier {
int
âge;
float
poids;
float
taille;
String couleur;
}
L'étape suivante consiste à ajouter des méthodes à notre classe. Avant de déclarer une méthode, il faut décider si elle prend des arguments et si elle retourne une valeur :
- La méthode dormir() ne fera qu'afficher le message « Bonne nuit, à demain » - elle n'a pas besoin d'arguments et ne retourne aucune valeur.
- Il en est de même pour la méthode manger(). Elle ne fera qu'afficher le message « J'ai si faim… Donne-moi un biscuit ! »
- La méthode dire() affichera aussi un message, mais l'animal pourra « dire » (afficher) le mot ou la phrase que nous lui fournirons. Nous passerons ce mot à la méthode dire() comme un argument de méthode. La méthode construira une phrase en utilisant cet argument et la retournera au programme appelant.
La nouvelle version de la classe AnimalFamilier ressemble à ceci :
public
class
AnimalFamilier {
int
âge;
float
poids;
float
taille;
String couleur;
public
void
dormir
(
) {
System.out.println
(
"Bonne nuit, à demain"
);
}
public
void
manger
(
) {
System.out.println
(
"J'ai si faim… Donne-moi un biscuit !"
);
}
public
String dire
(
String unMot) {
String réponseAnimal =
"OK !! OK !! "
+
unMot;
return
réponseAnimal;
}
}
Cette classe représente une sympathique créature du monde réel :
Voyons maintenant la signature de la méthode dormir():
public
void
dormir
(
)
Elle nous indique que cette méthode peut être appelée depuis n'importe quelle autre classe Java (public) et qu'elle ne retourne aucune donnée (void). Les parenthèses vides signifient que cette méthode ne prend pas d'argument, parce qu'elle n'a besoin d'aucune donnée du monde extérieur - elle affiche toujours le même texte.
La signature de la méthode dire() est celle-ci :
public
String dire
(
String unMot)
Cette méthode peut aussi être appelée depuis n'importe quelle autre classe Java, mais elle doit retourner un texte, ce qu'indique le mot-clé String devant le nom de la méthode. Par ailleurs, elle attend une donnée textuelle de l'extérieur, d'où l'argument String unMot.
Comment décider si une méthode doit retourner une valeur ou pas ? Si une méthode manipule des données et doit fournir le résultat de ces manipulations à une classe appelante, elle doit retourner une valeur. Tu vas me dire que notre classe AnimalFamilier n'a aucune classe appelante ! C'est exact, alors créons-en une, que nous appellerons MaîtreAnimal. Cette classe aura une méthode main() contenant le code nécessaire pour communiquer avec la classe AnimalFamilier. Crée simplement une autre classe nommée MaîtreAnimal, mais cette fois sélectionne dans Eclipse l'option qui crée la méthode main(). Rappelle-toi, sans cette méthode il est impossible d'exécuter cette classe en tant que programme. Modifie le code généré par Eclipse pour obtenir ceci :
public
class
MaîtreAnimal {
public
static
void
main
(
String[] args) {
String réactionAnimal;
AnimalFamilier monAnimal =
new
AnimalFamilier
(
);
monAnimal.manger
(
);
réactionAnimal =
monAnimal.dire
(
"Cui !! Cui !!"
);
System.out.println
(
réactionAnimal);
monAnimal.dormir
(
);
}
}
N'oublie pas d'appuyer sur Ctrl-S pour enregistrer et compiler cette classe !
Pour exécuter la classe MaîtreAnimal, sélectionne le menu Eclipse Exécuter, Exécuter…, Créer et tape le nom de la classe principale : MaîtreAnimal. Appuie sur le bouton Exécuter et le programme affichera le texte suivant :
J'ai si faim… Donne-moi un biscuit !
OK !! OK !! Cui !! Cui !!
Bonne nuit, à demain
La classe MaîtreAnimal est la classe appelante ; elle commence par créer une instance de l'objet AnimalFamilier. Elle déclare une variable monAnimal et utilise l'opérateur Java new :
AnimalFamilier monAnimal =
new
AnimalFamilier
(
);
Cette ligne déclare une variable du type AnimalFamilier (c'est exact, tu peux considérer chaque classe que tu crées comme un nouveau type Java). Maintenant, la variable monAnimal sait où a été créée l'instance d'AnimalFamilier dans la mémoire de l'ordinateur, et tu peux utiliser cette variable pour appeler n'importe laquelle des méthodes de la classe AnimalFamilier, par exemple :
monAnimal.dormir
(
);
Si une méthode retourne une valeur, tu dois l'appeler d'une autre façon. Déclare une variable du même type que la valeur retournée par la méthode. Tu peux maintenant appeler cette méthode :
String réactionAnimal;
réactionAnimal =
monAnimal.dire
(
"Cui !! Cui !!"
);
À ce stade, la valeur retournée est stockée dans la variable réactionAnimal et si tu veux voir ce qu'elle contient, ne te gêne pas :
System.out.println
(
réactionAnimal);
6-D. Héritage - un Poisson est aussi un AnimalFamilier▲
Notre classe AnimalFamilier va nous aider à découvrir un autre concept important de Java, appelé héritage. Dans la vie réelle, chaque personne hérite des caractéristiques de l'un ou l'autre de ses parents. De la même façon, dans le monde Java, tu peux, à partir d'une classe, en créer une nouvelle.
La classe AnimalFamilier possède un comportement et des attributs partagés par de nombreux animaux familiers - ils mangent, dorment, certains d'entre eux émettent des bruits, leurs peaux peuvent être de différentes couleurs, etc. D'un autre côté, les animaux familiers sont différents les uns des autres - les chiens aboient, les poissons nagent et sont silencieux, les perroquets parlent mieux que les chiens. Mais tous mangent, dorment, ont un poids et une taille. C'est pourquoi il est plus facile de créer une classe Poisson qui héritera certains comportements et attributs communs de la classe AnimalFamilier, que de créer Chien, Perroquet ou Poisson à partir de rien à chaque fois.
Le mot-clé spécial extends est là pour ça :
class
Poisson extends
AnimalFamilier {
}
On peut dire que notre Poisson est une sous-classe (subclass) de la classe AnimalFamilier et que la classe AnimalFamilier est une superclasse (superclass) de la classe Poisson. Autrement dit, on utilise la classe AnimalFamilier comme un modèle pour créer la classe Poisson.
Même si tu te contentes de laisser la classe Poisson telle qu'elle est maintenant, tu peux toujours utiliser chacun des attributs et méthodes hérités de la classe AnimalFamilier. Regarde :
Poisson monPetitPoisson =
new
Poisson
(
);
monPetitPoisson.dormir
(
);
Même si nous n'avons pas encore déclaré de méthode dans la classe Poisson, nous avons le droit d'appeler la méthode dormir() de sa superclasse !
Dans Eclipse, la création de sous-classes est une partie de plaisir ! Sélectionne le menu Fichier, Nouveau, Classe et tape Poisson comme nom de la classe. Remplace la valeur java.lang.Object par le mot AnimalFamilier dans le champ Superclasse.
N'oublions pas cependant que nous sommes en train de créer une sous-classe d'AnimalFamilier pour ajouter certaines propriétés que seuls les poissons possèdent et réutiliser une partie du code que nous avons écrit pour un animal familier en général.
Il est temps de te révéler un secret - toutes les classes Java héritent de la super superclasse Object, que tu utilises le mot extends ou pas. Mais une classe Java ne peut pas avoir deux parents distincts. Si c'était la même chose avec les gens, les enfants ne seraient pas des sous-classes de leurs parents, mais tous les garçons seraient des descendants d'Adam et toutes les filles des descendantes d'Ève.
Tous les animaux ne sont pas capables de plonger, mais il est certain que les poissons le peuvent. Ajoutons maintenant une nouvelle méthode plonger() à la classe Poisson.
public
class
Poisson extends
AnimalFamilier {
int
profondeurCourante =
0
;
public
int
plonger
(
int
combienDePlus){
profondeurCourante =
profondeurCourante +
combienDePlus;
System.out.println
(
"Plongée de "
+
combienDePlus +
" mètres"
);
System.out.println
(
"Je suis à "
+
profondeurCourante +
" mètres sous le niveau de la mer"
);
return
profondeurCourante;
}
}
La méthode plonger() a un argument combienDePlus qui indique au poisson de combien il doit plonger. Nous avons aussi déclaré la variable de classe profondeurCourante qui enregistrera la nouvelle profondeur courante à chaque fois que tu appelleras la méthode plonger(). Cette méthode renvoie la valeur courante de la variable profondeurCourante à la classe appelante.
Crée maintenant une autre classe nommée MaîtrePoisson qui ressemble à ceci :
public
class
MaîtrePoisson {
public
static
void
main
(
String[] args) {
Poisson monPoisson =
new
Poisson
(
);
monPoisson.plonger
(
2
);
monPoisson.plonger
(
3
);
monPoisson.dormir
(
);
}
}
La méthode main() instancie l'objet Poisson et appelle sa méthode plonger() deux fois, avec des arguments différents. Ensuite, elle appelle la méthode dormir(). Quand tu exécutes le programme MaîtrePoisson, il affiche les messages suivants :
Plongée de 2 mètres
Je suis à 2 mètres sous le niveau de la mer
Plongée de 3 mètres
Je suis à 5 mètres sous le niveau de la mer
Bonne nuit, à demain
As-tu noté que, outre la méthode définie dans la classe Poisson, le MaîtrePoisson appelle aussi des méthodes de sa superclasse AnimalFamilier ? C'est tout l'intérêt de l'héritage - tu n'as pas besoin de copier et coller le code de la classe AnimalFamilier - il suffit d'utiliser le mot extends et la classe Poisson peut utiliser les méthodes d'AnimalFamilier !
Encore une chose : bien que la méthode plonger() renvoie la valeur de profondeurCourante, notre MaîtrePoisson ne l'utilise pas. Très bien, notre MaîtrePoisson n'a pas besoin de cette valeur ; mais peut-être y a-t-il d'autres classes qui utilisent Poisson, qui pourraient trouver cette valeur utile. Par exemple, imagine une classe ContrôleurTraficPoissons qui doit connaître les positions des autres poissons dans la mer avant d'autoriser un poisson à plonger, pour éviter les accidents :-).
6-E. Redéfinition d'une méthode▲
Comme tu le sais, les poissons ne parlent pas (du moins ne parlent-ils pas à voix haute). Mais notre classe Poisson hérite de la classe AnimalFamilier qui possède la méthode dire(). Ceci signifie que rien ne t'empêche d'écrire quelque chose comme ça :
monPoisson.dire
(
"Un poisson qui parle !"
);
Eh bien, notre poisson a commencé à parler… Pour éviter que cela se produise, il faut que la classe Poisson redéfinisse (override) la méthode dire() de la classe AnimalFamilier. Voilà comment ça marche : si tu déclares une méthode avec exactement la même signature dans la sous-classe que dans la superclasse, la méthode de la sous-classe sera utilisée à la place de celle de la superclasse. Ajoutons la méthode dire() à la classe Poisson.
public
String dire
(
String unMot) {
return
"Ne sais-tu pas que les poissons ne parlent pas ?"
;
}
Ajoute maintenant l'appel suivant dans la méthode main() de la classe MaîtrePoisson :
String réactionPoisson;
réactionPoisson =
monPoisson.dire
(
"Salut"
);
System.out.println
(
réactionPoisson);
Exécute le programme et il affichera :
Ne sais-tu pas que les poissons ne parlent pas ?
Cela prouve que la méthode dire() de la classe AnimalFamilier a été redéfinie, ou, autrement dit, supprimée.
Si la signature d'une méthode inclut le mot-clé final, elle ne peut pas être redéfinie. Par exemple: final public void dormir(){…}
Waouh ! Nous en avons appris, des choses, dans ce chapitre. Que dirais-tu d'une petite pause ?
6-F. Autres lectures▲
1. Types de données Java : |
6-G. Exercices▲
1. Crée une nouvelle classe Voiture possédant les méthodes suivantes : |
6-H. Exercices pour les petits malins▲
|
Crée une sous-classe de Voiture nommée VoitureJamesBond et redéfinis la méthode rouler(). Utilise la formule suivante pour calculer la distance : |