Programmation Par Objets et Langage Java
Partie III. La Programmation Objets en Java
Najib Tounsi
Lien permanent : http://www.mescours.ma/Java/PooJavaPart-3-tdm.html
✔ Objets et Classes d'Objets
Une classe, est une implantation d'un TAD. Un TAD définit un ensemble d'objets caractérisé par les opérations qui leur sont applicables.
Encapsulation des données et des traitements
dans une classe, ici une pile
Champ : | Donnée faisant partie de la représentation d'un objet | |
Méthode : | Opération de manipulation d'un objet | |
Envoie de message : | demande d'exécution d'une méthode sur un objet |
Note: Champs et méthodes sont appelés membres (field member, method member) en Java et C++.
Une classe Article de commerce.
Classe Article champs numéro; nom; prixHT; qte; méthodes prixTTC () { return prixHT * 1.14; } ajouter (int q) { qte = qte + q; } retirer (int q) { qte = qte - q; } fin
Champs et Méthodes | ➪ |
caractéristiques (features). |
Identificateurs | ➪ | sélecteurs |
Champs dits aussi: attributs, propriétés |
A |
Classe Article |
B |
![]() |
En Java, on parle de variables d'instance (les champs) et de méthodes d'instance (les opérations). Voir plus loin, plus à ce sujet.
Dans une classe utilisatrice
Article a = new
Article();
a.ajouter (5);
x = b.prixTTC();
Envoi de message à un objet (appel d'une méthode d'instance). Trois notations:
envoyer_message
(a, ajouter, 5);
ajouter (a, q);
a.ajouter (5);
Intérêt de 3.
{a.qte =
a.qte + q;}
a
a.ajouter (5);
{ qte = qte + q;}
this
Appel opération | Méthode associée |
☟a.ajouter(q); |
Ajouter (entier q) {qte = qte + q;} |
this
) vers l'instance d'appel.
|
Article art = new Article(); p = art.prixHT; n = art.nom;
p = art.getPrixHT(); n = art.getNom();
classe Article
...
getPrixHT() { return prixHT;}
getNom() { return nom;} fin
getPrix()
,
getNom()
ou aPrixHT()
, aNom()
en français...Bonne pratique
Il est utile de ne laisser visibles à l'extérieur que certains sélecteurs (publiques) et de cacher les autres (privés). La bonne pratique c’est : seules les méthodes sont publiques.
Sans constructeurs (voir plus loin)
public class Article {
// Champs
private int numero = 0;
private String designation = new String("spécimen");
private double prixHT = 1.;
private int qte = 0;
// Méthodes
// Méthodes spécifiques
public double prixTTC () { return prixHT * 1.10; }
public void ajouter (int q) { qte = qte + q; }
public void retirer (int q) { qte = qte - q; }
// Fonctions d'accès...
public int getNumero(){return numero;}
public double getPrixHT(){return prixHT;}
public String getDesignation(){return designation;
public int getQte(){return qte;}
} // Fin classe
private
, comme il sied à "information hiding".Article art = new Article();
art
est créé contenant
<0, "spécimen", 1.0, 0>
les valeurs
fournies aux initialisations System.out.println (art.getDesignation()); // spécimen
System.out.println (art.getPrixHT()); // 1.0
System.out.println (art.prixTTC()); // 1.10
art.ajouter(5);
System.out.println (art.getQte()); // 5
this
. (voir classe page
précédente)public void ajouter (int qte) { this.qte = this.qte + qte; }
qte
masque le champ
qte
. On peut alors le qualifier avec
this
.La Programmation Orientée
Objets est la construction d'un système
logiciel comme une collection
structurée de classes. -- B. Meyer
--
classe Commande champs Article e; ... méthodes facturer(){ ... e.prixTTC(); } ... fin | ↗ | classe Article ... prixTTC () {...} ... fin |
Commande
utilise la classe
Article
, carfacturer
d'une commande
a besoin de connaître le prixTTC
d'un
articleimport
.import java.lang.*; // import du package lang
import java.lang.String; // import de la classe String
Article
public Article(){
numero = 0; // ou au autre numéro
designation = new String("@@@"); // idem
prixHT = 1.0 ; // idem
qte = 0; // idem
}
new
.Article art = new Article();
Article
public Article(int n, String m, double p, int q){
numero = n;
designation = new String(m);
prixHT = p;
qte = q;
}
Article art = new Article(10, "chemise", 249., 100 );
Article
, i.e. aucune méthode public
Article()
, alors l'instruction
new Article()
créerait une instance dont les
champs sont initialisé à 0
, false
ou null
.Object
.Article art2 = new Article(art),
public Article(Article a){
numero = a.getNumero();
designation = new String(a.getDesignation());
prixHT = a.getPrixHT();
qte = a.getQte();
}
designation
qui est un
objet String
; on instancie une nouvelle chaîne en y mettant la valeur du champ correspondant de a
)....
{ Article a = new Article ();
// l'article désigné par a est accessible
...
}
// a n'existe plus.
// La mémoire occupée par l'objet doit être libérée
null
à une
variable.Article mien = new Article (); ...
{
Article tien = mien;
// l'article est accessible par la référence tien
...
}
// la référence tien à disparu, mais l'article existe toujours!
// la référence mien est valable.
finalize()
, héritée de la classe
Object
, mais qui ne fait rien.
Un objet de classe B
est un objet de classe
A (aussi) s'il
possède les caractéristiques de A plus d'autres qui lui sont
propres.
NB. Article classe mère directe de Vêtement. Classe mère aussi de Chemise etc.
class Vetement extends Article {
private byte taille;
private String coloris;
};
extends
indique la super classe
de la classe en cours de définition.Vetement
rajoute deux champs
(taille
, coloris
) à ceux déjà
hérités de la classe mère
Article
et qui sont (numéro
,
designation
, prixHT
,
qte
).Vetement v = new Vetement();
v.ajouter(5); // La méthode héritée de la classe Article s'applique.
v
est crée par l'instruction
new Vetement()
.Article()
hérité.Vetement
(version 2) avec
constructeur qui initialise la partie propre à la sous
classe.class Vetement extends Article {
private byte taille;
private String coloris;
public Vetement(){
super();
taille = 6;
coloris = new String("blanc");
}
};
super
permet de faire appel
explicite au constructeur hérité de la classe mère
directe. Ici, le
constructeur défaut
Article()
.super
, l'appel au constructeur défaut
hérité est implicite
dans la méthode Vetement()
. Il faut bien
initialiser les champs hérités. L'absence de ce constructeur
dans la classe Article
provoque une erreur. On
n'appellera pas les constructeurs défauts ancêtres.super
permet entre autre de
choisir le constructeur parent à appeler. on pourrait écrire
super(1, "t-shirt", 10, 5)
pour appeler le
constructeur Article
() avec paramètres.class ArticleLuxe extends Article {
// ...
public double prixTTC () { return prixHT * 1.25; }
};
private
dans la classe mère
Article
.public
)
pour autant.protected
les champs de la
classe mère Article
.protected int numero = 0;
protected String nom = new String("spécimen");
protected double prixHT = 1.;
protected int qte;
protected
signifie donc
inaccessible pour les classes clientes (relation uses), accessible pour les sous
classes (relation isa).Dans une sous classe on peut donc:
(http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html
)
protected
dans la classe mère)super
.Article a = new Article(1, "Pomme", 10, 100 );
ArticleLuxe al = new ArticleLuxe(2, "iPhone", 200, 100);
a = al ; // affectation juste!
al = a ; // incompatible types
// found : Article required: ArticleLuxe
a ← al
, article reçoit article de
luxeArticleLuxe
a redéfini la méthode
prixTTC()
. Le calcul de la TVA est différent. Un
article de luxe est taxé à 25% au lieu de 10% pour un article
normal.System.out.println( a.prixTTC() ); // donne 11 ( 10 * 1.10)
System.out.println( al.prixTTC() ); // donne 250 (200 * 1.25)
Article
(a
ici) contient une
instance de ArticleLuxe
?a = al;
System.out.println( a.prixTTC() ); // donne 250 maintenant!
prixTTC()
s'applique à la variable
a
. Mais ne décide pas quel sera le code à
exécuter pour ça.a
, que sera cherché quelle méthode à
appliquer.Article[] chariot = new Article[3];
chariot [0] = new Article(1, "Pomme", 10, 100 );
chariot [1] = new ArticleLuxe(2, "iPhone", 200, 100);
chariot [2] = new Vetement(3, "Chemise", 30, 100, "Vert", XL );
prixTTC()
pour chaque article du chariot, et de faire la somme:double montant = 0;
for (Article p:chariot) {
montant += p.prixTTC();
}
System.out.println(montant); // 294
static void f(Article a){f () peut recevoir en paramètre effectif n'importe quel objet de la hiérarchie d'articles.
System.out.println(a.prixTTC());
}
class Commande {N'importe quel article peut faire l'objet d'une commande.
Article a;
// ...
}
Le polymorphisme est très important car il permet à une classe de déléguer à ses classes descendantes le fait de définir elles-même et selon leur besoin le code des méthodes héritées.
La classe Article
peut se contenter de définir
prixTTC()
avec un code par défaut et laisser les
sous classes Vetement
, ArticleLuxe
,
... le soin de fournir elles-même le code qui leur est
adapté.
Plus encore, la classe Article
peut se
contenter de déclarer prixTTC()
sans en
donner un code. Méthode abstraite. (Voir plus loin les classes
abstraites.)
static
Object
Object
est définie dans le package
Java.lang
.extends
) ou indirecte de
Object
.Object
.Object
...Trois méthodes particulièrement intéressantes sont:
protected Object clone() throws
CloneNotSupportedException
this
)
qui exécute.public boolean equals(Object obj)
this
) qui l'exécute.public String toString()
Object
Et aussi
protected void finalize() throws
Throwable
public final Class getClass()
public int hashCode()
Ainsi que d'autres méthodes liées au parallélisme en Java (programmation concurrente).
==
sert à faire ce
test.==
donnera vrai si deux variables sont la même
référence.Article a = new Article(2, "moto", 10, 10);
Article b = new Article(3, "vélo", 10, 10);
if (a==b) ... // toujours faux, car deux objets différents
// même si b peut être initialisé avec les mêmes valeurs
a = b;
if (a==b) ... // toujours vrai, exactement le même objet ("vélo" ici)
![]() |
![]() |
a et b deux objets
différentsa == b faux |
a et b désignent un
même objeta == b vrai |
equals()
, hérité de la
classe Object
, fait le test ==
si
elle est appliquée entre deux objets.if ( a.equals(b) ) ... // même résultat que ci-dessus
Article
la
méthode equals()
héritée.
a et b deux objets (distincts) de même
valeurs
(égalité en
profondeur)
Article
on redéfinit alors la méthode
héritée public boolean equals(Object obj)
, de
paramètre Object
.class Article {
...
public boolean equals (Object x){
// deux articles sont égaux s'ils ont le même numéro
return (numero == ((Article)x).getNumero());
}
}
Article a = new Article(2, "moto", 10, 10);
Article b = new Article(2, "moto", 10, 10);
System.out.println (a.equals(b)); // donnera true
Object
. Sinon, ce ne serait pas un Override
(cf. exercice ci-après)x
vers
Article
avant d'appliquer la méthode
getNumero()
.Le choix de
programmation de la méthode equals()
pour une classe utilisateur donnée dépend donc de
l'application: égalité des clés, égalité de toutes les
valeurs, etc.
Article
:public boolean equals (Article x){
return (numero == x.getNumero());
}
Object o = new Article(2, "moto", 10, 10);
// Object instancié avec un article
System.out.println (a.equals(o)); // false
// pourtant a et o ont le même n° article
a
et
o
ont les mêmes valeurs, et pourtant le test
répond faux. boolean equals(Article)
juste
ci-dessus, ne redéfinit
pas la méthode héritée de Object
, qui a le profile
boolean equals(Object)
et n'est donc pas
appelée.a.equals(o)
fera donc appel à la méthode
héritée boolean equals(Object)
qui va donc
tester les références si (a == o
), ce qui est
faux.Article
a = new Article(2, "moto", 10, 10);
b = a;
? (b
étant de classe
Article
)
![]() |
![]() |
![]() |
(1) Simple Copie | (2) Copie superficielle | (3) Copie en profondeur |
Article a = new Article(2, "moto", 10, 10);
Article b = new Article();
b = a;
Article
).Article b = new Article (a);
b
à partir d'un objet
a
.... // b déjà déclarée et utilisée
b = new Article (a);
clone()
héritée de la classe
Object
, à condition d'implémenter l'interface
Cloneable
, et qui soulève une exception.class Article implements Cloneable {... corps de Article ...}
public Object clone() throws CloneNotSupportedException {Ici on s'est contenté de faire usage de la méthode
return super.clone();
}
clone()
héritée.try {
b = (Article) a.clone();
}
catch (CloneNotSupportedException e){...}
b
a
reçu une copie de a
. Ce sont deux objets
différents mais de même valeur.public Object
clone()
redéfinie par
Article
.super.clone();
, de
faire appel à la méthode clone()
héritée de
Object
, qui crée un objet de la même classe que
l'original et initialise ses champs avec les mêmes valeurs
que l'original.nom
d'un
article.public Object clone() throws CloneNotSupportedException {
Article w = new Article();
w.numero = this.numero;
w.designation = new String(this.designation);
w.prixHT = this.prixHT;
w.qte = this.qte;
return w;
}
Article w
est créé, ses champs initialisés à partir de ceux de ceux de this
et est ensuite retourné.Article
pourrait ne pas donner de méthode de calcul du prix TTC, et
laisser chaque sous classe le faire à sa façon.abstract class Article {
// ...
abstract public double prixTTC ();
// ...
}
Article
ne sera plus instanciable.
A juste titre. On ne peut envoyer le message
prixTTC()
à une éventuelle
instance.abstract class FigureGeometrique {
int x, y;
// ...
void deplacer(int newX, int newY) {
// ...
}
abstract void dessiner();
abstract int surface();
}
class Cercle extends FigureGeometrique { void dessiner() { ... } int surface() { ... } }
class Rectangle extends FigureGeometrique { void dessiner() { ... } int surface() { ... } }
final
.final int surface(){
...
}
Object
fait cela pour certaines de ses
méthodes (e.g. getClass, notify ...).interface Pile {
final int MAX = 8;
public void empiler(char c);
public char sommet();
// ...
};
implements
(en français réaliser, mettre en oeuvre).class PileTableau implements Pile {
char[] t = new char[MAX];
int top=-1;
public void empiler(char c) {
t[++top] = c;
}
public char sommet() {
return t[top];
}
};
class PileTableau
) avec un
tableau de
caractères.interface Transport {
public int prixTransport();
...
}
interface Fragile extends Transport { // héritage d'interface!
public double prixTransport();
...
}
class Article {...}
class ArticleLuxe extends Article {...}
class Televiseur extends ArticleLuxe implements Fragile {
public double prixTransport() {
// Calcul effectif du prix transport de téléviseur
}
...
}
Televiseur
hérite des caractéristiques
ArticleLuxe
(et donc aussi Article
), et elle doit réaliser la méthode prixTransport()
"héritée". AlimElectrique
.class Televiseur extends ArticleLuxe implements Fragile, AlimElectrique {
public double prixTransport() {
// Calcul effectif du prix transport televiseur
}
public double consommation() {
// Calcul de la consommation de téléviseur
}
...
}
interface AlimElectrique {
int voltage = 120;
public double consommation();
...
}
voltage
est ici une constante déclarée
final
.