Document Java 9
Document Java 9
Document Java 9
seule classe. Vous allez apprendre qu'en faisant de la programmation orientée objet, nous travaillerons
en fait avec de nombreuses classes. Rappelez-vous la première partie : vous avez déjà utilisé des objets…
Oui ! Lorsque vous faisiez ceci : String str = new String("tiens… un objet String");.
Ici str est un objet String. Vous avez utilisé un objet de la classe String : on dit que vous avez créé une
instance de la classeString(). Le moment est venu pour vous de créer vos propres classes.
Structure de base
Une classe peut être comparée à un moule qui, lorsque nous le remplissons, nous donne un objet ayant
la forme du moule ainsi que toutes ses caractéristiques. Comme quand vous étiez enfants, lorsque vous
vous amusiez avec de la pâte à modeler.
Si vous avez bien suivi la première partie de ce cours, vous devriez savoir que notre classe contenant la
méthode main ressemble à ceci :
class ClasseMain{
Nommez votre classe « Ville » (avec un « V » majuscule, convention de nommage oblige). Cette fois, vous
ne devez pas y créer la méthode main.
Il ne peut y avoir qu'une seule méthode main active par projet ! Souvenez-vous que celle-ci est le point
de départ de votre programme. Pour être tout à fait précis, plusieurs méthodes main peuvent cohabiter
dans votre projet, mais une seule sera considérée comme le point de départ de votre programme !
Classe Ville
Classe Ville
Ici, notre classe Ville est précédée du mot clé public. Vous devez savoir que lorsque nous créons une
classe comme nous l'avons fait, Eclipse nous facilite la tâche en ajoutant automatiquement ce mot clé,
qui correspond à la portée de la classe. Retenez pour l'instant que public class UneClasse{} et class
UneClasse{} sont presque équivalents !
En programmation, la portée détermine qui peut faire appel à une classe, une méthode ou une variable.
Vous avez déjà rencontré la portée public : cela signifie que tout le monde peut faire appel à l'élément.
Ici dans le cas qui nous intéresse il s'agit d'une méthode. Une méthode marquée comme public peut
donc être appelée depuis n'importe quel endroit du programme.
Nous allons ici utiliser une autre portée : private. Elle signifie que notre méthode ne pourra être appelée
que depuis l'intérieur de la classe dans laquelle elle se trouve ! Les méthodes déclarées private
correspondent souvent à des mécanismes internes à une classe que les développeurs souhaitent «
cacher » ou simplement ne pas rendre accessibles de l'extérieur de la classe…
Il en va de même pour les variables. Nous allons voir que nous pouvons protéger des variables grâce au
mot clé private. Le principe sera le même que pour les méthodes. Ces variables ne seront alors
accessibles que dans la classe où elles seront nées…
Bon. Toutes les conditions sont réunies pour commencer activement la programmation orientée objet !
Et si nous allions créer notre première ville ?
Les constructeurs
Vu que notre objectif dans ce chapitre est de construire un objet Ville, il va falloir définir les données
qu'on va lui attribuer. Nous dirons qu'un objet Ville possède :
Nous allons faire ceci en mettant des variables d'instance (de simples variables identiques à celles que
vous manipulez habituellement) dans notre classe. Celle-ci va contenir une variable dont le rôle sera de
stocker le nom, une autre stockera le nombre d'habitants et la dernière se chargera du pays ! Voici à quoi
ressemble notre classe Ville à présent :
String nomVille;
String nomPays;
int nbreHabitants;
Contrairement aux classes, les variables d'instance présentes dans une classe sont public si vous ne leur
spécifiez pas de portée. Alors, on parle de variable d'instance, parce que dans nos futures classes Java
qui définiront des objets, il y aura plusieurs types de variables (nous approfondirons ceci dans ce
chapitre). Pour le moment, sachez qu'il y a trois grands types de variables dans une classe objet :
Les variables d'instance : ce sont elles qui définiront les caractéristiques de notre objet.
Les variables de classe : celles-ci sont communes à toutes les instances de votre classe.
Les variables locales : ce sont des variables que nous utiliserons pour travailler dans notre objet.
Dans l'immédiat, nous allons travailler avec des variables d'instance afin de créer des objets différents. Il
ne nous reste plus qu'à créer notre premier objet, pour ce faire, nous allons devoir utiliser ce qu'on
appelle des constructeurs.
Un constructeur est une méthode d'instance qui va se charger de créer un objet et, le cas échéant,
d'initialiser ses variables de classe ! Cette méthode a pour rôle de signaler à la JVM (Java Virtual
Machine) qu'il faut réserver de la mémoire pour notre futur objet et donc, par extension, d'en réserver
pour toutes ses variables.
Notre premier constructeur sera ce qu'on appelle communément un constructeur par défaut, c'est-à-
dire qu'il ne prendra aucun paramètre, mais permettra tout de même d'instancier un objet, et vu que
nous sommes perfectionnistes, nous allons y initialiser nos variables d'instance. Voici votre premier
constructeur :
public class Ville{
String nomVille;
String nomPays;
int nbreHabitants;
public Ville(){
nomVille = "Inconnu";
nomPays = "Inconnu";
nbreHabitants = 0;
Vous avez remarqué que le constructeur est en fait une méthode qui n'a aucun type de retour (void,
double…) et qui porte le même nom que notre classe ! Ceci est une règle immuable : le (les)
constructeur(s) d'une classe doit (doivent) porter le même nom que la classe !
Son corollaire est qu'un objet peut avoir plusieurs constructeurs. Il s'agit de la même méthode, mais
surchargée ! Dans notre premier constructeur, nous n'avons passé aucun paramètre, mais nous allons
bientôt en mettre.
Vous pouvez d'ores et déjà créer une instance de Ville. Cependant, commencez par vous rappeler qu'une
instance d'objet se fait grâce au mot clé new, comme lorsque vous créez une variable de type String.
Maintenant, vu que nous allons créer des objets Ville, nous allons procéder comme avec les String.
Vérifions que l'instanciation s’effectue comme il faut. Allons dans notre classe contenant la méthode
main et instancions un objet Ville. Je suppose que vous avez deviné que le type de notre objet sera Ville !
Exécutez ce code, vous devriez avoir l'équivalent de la figure suivante sous les yeux.
Maintenant, nous devons mettre des données dans notre objet, ceci afin de pouvoir commencer à
travailler… Le but sera de parvenir à une déclaration d'objet se faisant comme ceci :
Vous avez remarqué qu'ici, les paramètres sont renseignés : eh bien il suffit de créer une méthode qui
récupère ces paramètres et initialise les variables de notre objet, ce qui achèvera notre constructeur
d'initialisation.
Voici le constructeur de notre objet Ville, celui qui permet d'avoir des objets avec des paramètres
différents :
String nomPays;
int nbreHabitants;
public Ville(){
nomVille = "Inconnu";
nomPays = "Inconnu";
nbreHabitants = 0;
}
//Constructeur avec paramètres
//Ce n'est pas une convention, mais ça peut être un bon moyen de les repérer.
nomVille = pNom;
nomPays = pPays;
nbreHabitants = pNbre;
}
Dans ce cas, l'exemple de déclaration et d'initialisation d'un objet Ville que je vous ai montré un peu plus
haut fonctionne sans aucun souci ! Mais il vous faudra respecter scrupuleusement l'ordre des
paramètres passés lors de l'initialisation de votre objet : sinon, c'est l'erreur de compilation à coup sûr !
Ainsi :
Par contre, notre objet présente un gros défaut : les variables d'instance qui le caractérisent sont
accessibles dans votre classe contenant votre main ! Ceci implique que vous pouvez directement
modifier les attributs de la classe. Testez ce code et vous verrez que le résultat est identique à la figure
suivante :
System.out.println(ville.nomVille);
System.out.println(ville2.nomPays);
Vous constatez que nous pouvons accéder aux variables d'instance en utilisant le « . », comme lorsque
vous appelez la méthode subString() de l'objet String. C'est très risqué, et la plupart des programmeurs
Java vous le diront. Dans la majorité des cas, nous allons contrôler les modifications des variables de
classe, de manière à ce qu'un code extérieur ne fasse pas n'importe quoi avec nos objets ! En plus de ça,
imaginez que vous souhaitiez faire quelque chose à chaque fois qu'une valeur change ; si vous ne
protégez pas vos données, ce sera impossible à réaliser… C'est pour cela que nous protégeons nos
variables d'instance en les déclarant private, comme ceci :
//…
Désormais, ces attributs ne sont plus accessibles en dehors de la classe où ils sont déclarés ! Nous allons
maintenant voir comment accéder tout de même à nos données.
Accesseurs et mutateurs
Un accesseur est une méthode qui va nous permettre d'accéder aux variables de nos objets en lecture,
et un mutateur nous permettra d'en faire de même en écriture ! Grâce aux accesseurs, vous pourrez
afficher les variables de vos objets, et grâce aux mutateurs, vous pourrez les modifier :
public class Ville {
return nomVille;
{
return nomPays;
return nbreHabitants;
nomVille = pNom;
nomPays = pPays;
nbreHabitants = nbre;
}
Nos accesseurs sont bien des méthodes, et elles sont public pour que vous puissiez y accéder depuis une
autre classe que celle-ci : depuis le main, par exemple. Les accesseurs sont du même type que la variable
qu'ils doivent retourner. Les mutateurs sont, par contre, de type void. Ce mot clé signifie « rien » ; en
effet, ces méthodes ne retournent aucune valeur, elles se contentent de les mettre à jour.
Je vous ai fait faire la différence entre accesseurs et mutateurs, mais généralement, lorsqu'on parle
d'accesseurs, ce terme inclut également les mutateurs. Autre chose : il s'agit ici d'une question de
convention de nommage. Les accesseurs commencent par get et les mutateurs par set, comme vous
pouvez le voir ici. On parle d'ailleurs parfois de Getters et de Setters.
/*
*/
temp = v1;
v1 = v2;
v2 = temp;
/*
*/
v1.setNom("Hong Kong");
v2.setNom("Djibouti");
Vous voyez bien que les constructeurs ont fonctionné, que les accesseurs tournent à merveille et que
vous pouvez commencer à travailler avec vos objets Ville. Par contre, pour afficher le contenu, on
pourrait faire plus simple, comme par exemple créer une méthode qui se chargerait de faire tout ceci…
Je sais ce que vous vous dites : « Mais les accesseurs, ce ne sont pas des méthodes ? ». Bien sûr que si,
mais il vaut mieux bien distinguer les différents types de méthodes dans un objet :
les accesseurs -> méthodes servant à accéder aux données des objets ;
Avec nos objets Ville, notre choix est un peu limité par le nombre de méthodes possibles, mais nous
pouvons tout de même en faire une ou deux pour l'exemple :
faire un système de catégories de villes par rapport à leur nombre d'habitants ( <1000 -> A, <10 000 ->
B…). Ceci est déterminé à la construction ou à la redéfinition du nombre d'habitants : ajoutons donc une
variable d'instance de type char à notre classe et appelons-la categorie. Pensez à ajouter le traitement
aux bons endroits ;
une méthode pour comparer deux objets par rapport à leur nombre d'habitants.
Nous voulons que la classe Ville gère la façon de déterminer la catégorie elle-même, et non que cette
action puisse être opérée de l'extérieur. La méthode qui fera ceci sera donc déclarée private.
Par contre, un problème va se poser ! Vous savez déjà qu'en Java, on appelle les méthodes d'un objet
comme ceci : monString.subString(0,4);. Cependant, vu qu'il va falloir qu'on travaille depuis l'intérieur de
notre objet, vous allez encore avoir un mot clé à retenir : cette fois, il s'agit du...
Je vous propose de voir le mot-clé this en action dans le code de notre classe Ville en entier, c'est-à-dire
comportant les méthodes dont on vient de parler :
public Ville(){
nomVille = "Inconnu";
nomPays = "Inconnu";
nbreHabitants = 0;
this.setCategorie();
nomVille = pNom;
nomPays = pPays;
nbreHabitants = pNbre;
this.setCategorie();
}
return nomVille;
return nomPays;
return nbreHabitants;
return categorie;
nomVille = pNom;
nomPays = pPays;
{
nbreHabitants = nbre;
this.setCategorie();
int bornesSuperieures[] = {0, 1000, 10000, 100000, 500000, 1000000, 5000000, 10000000};
char categories[] = {'?', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'};
int i = 0;
i++;
this.categorie = categories[i];
return "\t"+this.nomVille+" est une ville de "+this.nomPays+ ", elle comporte : "+this.nbreHabitants+"
habitant(s) => elle est donc de catégorie : "+this.categorie;
return str;
Pour simplifier, this fait référence à l'objet courant ! Bien que la traduction anglaise exacte soit « ceci », il
faut comprendre « moi ». À l'intérieur d'un objet, ce mot clé permet de désigner une de ses variables ou
une de ses méthodes.
Pour expliciter le fonctionnement du mot clé this, prenons l'exemple de la méthode comparer(Ville V1).
La méthode va s'utiliser comme suit :
Dans cette méthode, nous voulons comparer le nombre d'habitants de chacun des deux objets Ville.
Pour accéder à la variable nbreHabitants de l'objet V2, il suffit d'utiliser la syntaxe
V2.getNombreHabitants() ; nous ferons donc référence à la propriété nbreHabitants de l'objet V2.
Mais l'objet V, lui, est l'objet appelant de cette méthode. Pour se servir de ses propres variables, on
utilise alors this.nbreHabitants, ce qui a pour effet de faire appel à la variable nbreHabitants de l'objet
exécutant la méthode comparer(Ville V).
Explicitons un peu les trois méthodes qui ont été décrites précédemment.
La méthode categorie()
Elle ne prend aucun paramètre, et ne renvoie rien : elle se contente de mettre la variable de classe
categorie à jour. Elle détermine dans quelle tranche se trouve la ville grâce au nombre d'habitants de
l'objet appelant, obtenu au moyen du mot clé this. Selon le nombre d'habitants, le caractère renvoyé
changera. Nous l'appelons lorsque nous construisons un objet Ville (que ce soit avec ou sans paramètre),
mais aussi lorsque nous redéfinissons le nombre d'habitants : de cette manière, la catégorie est
automatiquement mise à jour, sans qu'on ait besoin de faire appel à la méthode.
La méthode decrisToi()
Celle-ci nous renvoie un objet de type String. Elle fait référence aux variables qui composent l'objet
appelant la méthode, toujours grâce à this, et nous renvoie donc une chaîne de caractères qui nous
décrit l'objet en énumérant ses composants.
Elle prend une ville en paramètre, pour pouvoir comparer les variables nbreHabitants de l'objet appelant
la méthode et de celui passé en paramètre pour nous dire quelle ville est la plus peuplée ! Et si nous
faisions un petit test ?
System.out.println("\n\n"+v1.decrisToi());
System.out.println(v.decrisToi());
System.out.println(v2.decrisToi()+"\n\n");
System.out.println(v1.comparer(v2));
Je viens d'avoir une idée : et si nous essayions de savoir combien de villes nous avons créées ? Comment
faire ? Avec une variable de classe !
Comme je vous le disais au début de ce chapitre, il y a plusieurs types de variables dans une classe. Nous
avons vu les variables d'instance qui forment la carte d'identité d'un objet ; maintenant, voici les
variables de classe.
Celles-ci peuvent s'avérer très utiles. Dans notre exemple, nous allons compter le nombre d'instances de
notre classe Ville, mais nous pourrions les utiliser pour bien d'autres choses (un taux de TVA dans une
classe qui calcule le prix TTC, par exemple).
La particularité de ce type de variable, c'est qu'elles seront communes à toutes les instances de la
classe !
Créons sans plus attendre notre compteur d'instances. Il s'agira d'une variable de type int que nous
appellerons nbreInstance, et qui sera public ; nous mettrons aussi son homologue en private en place et
l'appellerons nbreInstanceBis (il sera nécessaire de mettre un accesseur en place pour cette variable).
Afin qu'une variable soit une variable de classe, elle doit être précédée du mot clé static. Cela donnerait
dans notre classe Ville :
public Ville(){
nbreInstances++;
nbreInstancesBis++;
nbreInstances++;
nbreInstancesBis++;
//Le reste ne change pas
return nbreInstancesBis;
Vous avez dû remarquer que l'accesseur de notre variable de classe déclarée privée est aussi déclaré
static : ceci est une règle ! Toutes les méthodes de classe n'utilisant que des variables de classe doivent
être déclarées static. On les appelle des méthodes de classe, car il n'y en a qu'une pour toutes vos
instances. Par contre ce n’est plus une méthode de classe si celle-ci utilise des variables d'instance en
plus de variables de classe…
À présent, si vous testez le code suivant, vous allez constater l'utilité des variables de classe :
Le résultat, visible à la figure suivante, montre que le nombre augmente à chaque instanciation.
Lorsque vous avez vu les méthodes, vous les avez déclarées public. Vous auriez également pu les
déclarer private, mais attention, dans les deux cas, il faut aussi qu'elles soient static, car elles sont
exécutées dans un contexte static : la méthode main.
Le principe d'encapsulation
Voilà, vous venez de construire votre premier objet « maison ». Cependant, sans le savoir, vous avez fait
plus que ça : vous avez créé un objet dont les variables sont protégées de l'extérieur. En effet, depuis
l'extérieur de la classe, elles ne sont accessibles que via les accesseurs et mutateurs que nous avons
défini. C'est le principe d'encapsulation !
En fait, lorsqu'on procède de la sorte, on s'assure que le fonctionnement interne à l'objet est intègre, car
toute modification d'une donnée de l'objet est maîtrisée. Nous avons développé des méthodes qui
s'assurent qu'on ne modifie pas n'importe comment les variables.
Prenons l'exemple de la variable nbreHabitants. L'encapsuler nous permet, lors de son affectation, de
déduire automatiquement la catégorie de l'objet Ville, chose qui n'est pas facilement faisable sans
encapsulation. Par extension, si vous avez besoin d'effectuer des opérations déterminées lors de
l'affectation du nom d'une ville par exemple, vous n'aurez pas à passer en revue tous les codes source
utilisant l'objet Ville : vous n'aurez qu'à modifier l'objet (ou la méthode) en question, et le tour sera joué.
Si vous vous demandez l'utilité de tout cela, dites-vous que vous ne serez peut-être pas seuls à
développer vos logiciels, et que les personnes utilisant vos classes n'ont pas à savoir ce qu'il s'y passe :
seules les fonctionnalités qui leurs sont offertes comptent. Vous le verrez en continuant la lecture de cet
ouvrage, Java est souple parce qu'il offre beaucoup de fonctionnalités pouvant être retravaillées selon
les besoins, mais gardez à l'esprit que certaines choses vous seront volontairement inaccessibles, pour
éviter que vous ne « cassiez » quelque chose.
Une classe permet de définir des objets. Ceux-ci ont des attributs (variables d'instance) et des
méthodes (méthodes d’instance + accesseurs).
Le ou les constructeurs d'une classe doivent porter le même nom que la classe et n'ont pas de type de
retour.
L'ordre des paramètres passés dans le constructeur doit être respecté.
Il est recommandé de déclarer ses variables d'instance private, pour les protéger d'une mauvaise
utilisation par le programmeur.
On crée des accesseurs et mutateurs (méthodes getters et setters) pour permettre une modification
sûre des variables d'instance.
Dans une classe, on accède aux variables de celle-ci grâce au mot clé this.
Une variable de classe est une variable devant être déclarée static.
Les méthodes n'utilisant que des variables de classe doivent elles aussi être déclarées static.