Вы находитесь на странице: 1из 156

Support de cours Java

Structures de donnes
Notions en Gnie Logiciel
et Programmation Oriente Objet
H. Mounier
Universit Paris Sud

Table des matires


Table des matires
I

II

Threads
I.1
Modle de threads de Java . . . . . . .
I.2
Cration et utilitaires . . . . . . . . . .
I.3
Synchronisation . . . . . . . . . . . . .
I.4
Groupes de threads . . . . . . . . . . .
I.5
tats dune thread et ordonnancement

.
.
.
.
.

1
1
3
7
15
20

Gnie logiciel appliqu


II.1
Implantation dune pile en Java . . . . . . . . . . . . . . . . . .
II.2
Minimisation de couplage entre mthodes . . . . . . . . . . . . .
II.3
Maximisation de cohsion des mthodes . . . . . . . . . . . . .

25
25
32
39

III Programmation oriente objet en Java


III.1 Exemple 1 : balles . . . . . . . . . . . .
III.2 Exemple 2 : Flipper . . . . . . . . . . .
III.3 Formes dhritage . . . . . . . . . . . .
III.4 Exemple 3 : jeu de solitaire . . . . . .
IV Botes outils awt et Swing
IV.1 Aperu des classes . . . . . . . .
IV.2 Gestion des vnements . . . . .
IV.3 Gestionnaire de disposition . . . .
IV.4 Composants dinterface utilisateur
IV.5 Un exemple . . . . . . . . . . . .
IV.6 Botes de dialogue . . . . . . . . .
IV.7 Menus . . . . . . . . . . . . . . .
V

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.

.
.
.
.

. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
spcifiquement Awt
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .

.
.
.
.
.

.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.

.
.
.
.
.
.
.

.
.
.
.

49
49
56
63
71

.
.
.
.
.
.
.

85
85
90
93
95
103
106
108

Classe Url
111
V.1
Classe URL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
i

ii
VI Paquetage java.net : Sockets
VI.1 Sockets : Rappels TCP/IP .
VI.2 Sockets : Clients . . . . . . .
VI.3 Sockets serveurs . . . . . . .
VI.4 Serveurs itratifs . . . . . .
VI.5 Serveurs non bloquants . . .
VI.6 Serveurs concurrents . . . .

Support de cours Java

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

115
115
116
125
128
130
132

VII Invotaion de mthodes distantes : RMI


VII.1 Notion dinvocation de mthode distante . . .
VII.2 Scurit Srialisation . . . . . . . . . . . . .
VII.3 Brve description du fonctionnement des RMI
VII.4 Implantation . . . . . . . . . . . . . . . . . .
VII.5 Paquetage java.rmi . . . . . . . . . . . . . . .
VII.6 Paquetage java.rmi.registry . . . . . . . . . .
VII.7 Paquetage java.rmi.server . . . . . . . . . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

135
135
136
138
140
144
148
149

Bibliographie

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

151

I Threads
Rfrences bibliographiques
The Java Language Specification, J. Gosling, B. Joy et G. Steele [GJS96],
Java Threads, S. Oaks et J. Wong [OW97],
Concurrent Programming in Java. Design Principles and Patterns, D. Lea
[Lea97].

I.1

Modle de threads de Java

1.1 Threads et processus


Le multi-tches de threads occasionne bien moins de surcharge que le multitches de processus
Processus :
Un ensemble de ressources ; par ex. sous UNIX, segment de code, de donnes utilisateur, de donnes systmes (rpertoire de travail, descripteurs
de fichiers, identificateurs de lutilisateur ayant lanc le processus, du
groupe dont il fait partie, infos. sur lemplacment des donnes en mmoire, . . . )
Une unit dexcution (avec, par ex. sous UNIX, des informations ncessaires lordonnanceur, la valeur des registres, le compteur dinstructions,
la pile dexcution, des informations relatives aux signaux)
Thread : on ne retient que laspect dunit dexcution.
Contrairement aux processus, les threads sont lgres :
elles partagent le mme espace dadressage,
elles existent au sein du mme processus (lourd),
la communication inter-thread occasionne peu de surcharge,
le passage contextuel (context switching) dune thread une autre est
peu coteux.
1

Support de cours Java


Le multi-tches de processus nest pas sous le contrle de lenvironnement
dexcution java. Par contre, il y a un mcanisme interne de gestion multithreads.
Le peu de surcharge occasionn par le systme de multi-threads de Java
est spcialement intressant pour les applications distribues. Par ex., un
serveur multi-tches (avec une tche par client servir)

1.2 Etats dune thread


Dans un environnement mono-thread, lorsquune thread se bloque (voit son
excution suspendue) en attente dune ressource, le programme tout entier
sarrte. Ceci nest plus le cas avec un environnement multi-threads
Une thread peut tre :
en train de sexcuter (running) ;
prte sexcuter (ready to run), ds que la CPU est disponible ;
suspendue (suspended), ce qui stoppe temporairement son activit ;
poursuivre lexcution (resumed), l o elle a t suspendue ;
bloque (blocked) en attendant une ressource.
Une thread peut se terminer tout moment, ce qui arrte son excution
immdiatement. Une fois termine, une thread ne peut tre remise en route

1.3 Priorits dune thread


Une thread peut
soit volontairement laisser le contrle. Ceci est ralis en passant le contrle
explicitement, dormant ou bloquant en E/S. La thread prte sexcuter
et de plus haute priorit est alors lance.
soit tre supplante par une autre de plus haute priorit. Si cest systmatiquement le cas, on parle de multi-tches premptif.
Dans le cas o 2 threads de mme priorit veulent sexcuter, le rsultat est
dpendant du systme dexploitation (de son algorithme dordonnancement).
Sous Windows 95, le mme quantum de temps est tour tour allou aux
threads de mme priorit.
Sous Solaris 2.x, des threads de mme priorit doivent explicitement passer
le contrle leurs pairs . . .

1.4 Synchronisation
Synchronisation inter-threads via des moniteurs. Notion dfinie par C.A.R
Hoare.

I.2 Cration et utilitaires

Moniteur : mini bote ne pouvant contenir quune thread. Une fois quune
thread y est entre, les autres doivent attendre quelle en sorte.
Pas de classe Monitor. Chaque objet a son propre moniteur implicite
dans lequel on entre automatiquement lorsquune mthode synchronise de
(linstance de) lobjet est appele.
Lorsquune thread est dans une mthode synchronise, aucune autre thread
ne peut appeler de mthode synchronise de la mme instance de lobjet.
Permet un code clair et compact, la synchronisation tant construite dans
le langage.
Il est galement possible pour 2 threads de communiquer par un mcanisme
simple et souple.

I.2

Cration et utilitaires

2.1 Classe Thread et interface Runnable


Le systme de multi-threads de Java est construit autour de la classe Thread
et de son interface compagnon Runnable.
Liste des mthodes les plus courantes :
methode()

getName()
getPriority()
isAlive()
join()
resume()
run()
sleep()
start()
suspend()

2.2 Cration dune thread


Deux possibilits :
Implanter linterface Runnable,

But
Obtenir le nom dune thread
Obtenir la priorit dune thread
Dterminer si une thread est toujours en
cours dexcution
Attendre la terminaison dune thread
Poursuivre lexcution dune thread suspendue
Point dentre (dexcution) de la thread
Suspendre la thread pour une priode de
temps donne
Dbuter une thread en appelant sa mthode run()
Suspendre une thread

Support de cours Java


Crer une sous-classe de Thread
Pour implanter Runnable, il y a seulement besoin dimplanter la mthode
run() :
public abstract void run()

Dans run(), on place les instructions de la thread lancer.


Ayant cr une classe implantant Runnable, on cr une instance de Thread
dans cette classe :
Thread(Runnable objThread, String nomThread)

La thread ne dbute pas tant quon nappelle pas start().


En fait start() effectue un appel run(). Cest un cadre logiciel.
synchronized void start()
class NouvelleActivite implements Runnable {
Thread th;
NouvelleActivite() {
th = new Thread(this, "Activite demo");
System.out.println("Activite enfant : " + th);
th.start();
// Debuter lactivite
}
// Point dentree de la nouvelle activite
public void run() {
try {
for(int i = 5; i > 0; i--) {
System.out.println("Activite enfant : " + i);
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.out.println("Enfant interrompu.);
}
System.out.println("Sortie de lactivite enfant");
}// run()
}// class NouvelleActivite
class ActiviteDemo {
public static void main(String args[]) {
new NouvelleActivite();
try {
for(int i = 5; i > 0; i--) {
System.out.println("Activite parente : " + i);

I.2 Cration et utilitaires

Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("Parent interrompu.);
}
System.out.println("Sortie de lactivite parent");
}// run()
}// class Activite

2.3 Cration dune thread : hriter de Thread


Deuxime possibilit : crer une sous-classe de Thread, qui redfinit la
mthode run(). Mme exemple :
class NouvelleActivite extends Thread {
NouvelleActivite() {
super("Activite demo");
System.out.println("Activite enfant : " + this);
start();
// Debuter lactivite
}
// Point dentree de la nouvelle activite
public void run() {
try {
for(int i = 5; i > 0; i--) {
System.out.println("Activite enfant : " + i);
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.out.println("Enfant interrompu.);
}
System.out.println("Sortie de lactivite enfant");
}// run()
}// class NouvelleActivite
class ActiviteDemo {
public static void main(String args[]) {
new NouvelleActivite();
try {
for(int i = 5; i > 0; i--) {
System.out.println("Activite parente : " + i);

Support de cours Java


Thread.sleep(1000);

}
} catch (InterruptedException e) {
System.out.println("Parent interrompu.);
}
System.out.println("Sortie de lactivite parent");
}// run()
}// class Activite

Que choisir entre sous-classer Thread et implanter Runnable ? La classe


Thread dfinit plusieurs mthodes pouvant tre redfinies par des classes
drives ; la seule qui doit ltre est run().
Divers programmeurs java estiment quon tend ( extends) une classe seulement pour la modifier ou la complter. Si lon ne doit pas redfinir
dautres mthodes que run(), plutt implanter Runnable.

2.4 Mthodes utilitaires


Ci-dessus figure sleep() :
static void sleep(long millisec) throws InterruptedException

qui suspend lexcution de la thread appelante pendant millisec milisecondes. Elle peut dclencher une exception
Pour obtenir et changer le nom dune thread, on dispose de :
final void setName(String nomThread)
final String getName()

Deux moyens pour savoir quand une thread est termine :


final boolean isAlive() throws InterruptedException

qui renvoie true si la thread appelante est toujours en cours dexcution ;


elle renvoie false sinon ;
final void join() throws InterruptedException

qui attend que la thread appelante se termine.


Suspension et poursuite dexcution :
final void suspend()
final void resume()

Obtention et modification de priorit :


final void setPriority(int niveau)

qui modifie la priorit niveau, ce dernier tant entre MIN_PRIORITY


et MAX_PRIORITY (qui sont souvent 1 et 10) ; la priorit par dfaut est
NORM_PRIORITY (gnralement 5) ;

I.3 Synchronisation

final int getPriority()

Sous Solaris, il y a un comportement multi-tches non premptif (les tches


plus prioritaires doivent laisser le contrle aux moins prioritaires pour que
ces dernires aient une chance de tourner). Pour avoir un comportement prdictible sur diverses plates-formes, utiliser des threads qui laissent
volontairement le contrle aux autres.

I.3

Synchronisation

3.1 Synchronisation
Lorsque 2 threads ou plus ont besoin dune mme ressource au mme moment, il y a besoin de sassurer que la ressource ne sera utilise que par une
thread en mme temps : on utilise alors un procd de synchronistaion.
Un moyen dobtenir une synchronisation : les moniteurs.
Un moniteur est un mcanisme utilis comme un verrou dexclusion mutuelle, un mutex.
Lorsquune thread acquiert un verrou, on dit quelle entre dans le moniteur.
Les autres threads sont dites en attente du moniteur.
Analogie avec des cabines dessayage de vtements.
Deux moyens de synchronisation : les mthodes synchronises et les blocs
synchroniss.

3.2 Mthodes synchronises


Pour entrer dans le moniteur dun objet, appeler une mthode modifie avec
le mot cl synchronized.
Lorsquune thread est dans une mthode synchronise, toute autre thread
qui essaie de lappeler (elle ou toute autre mthode synchronise) doit attendre.
Pour sortir du moniteur, celui qui y est entr revient simplement de la
mthode synchronise.
Exemple de programme non synchronis. Dans une classe Callme, la mthode call() essaie dafficher une chane String passe en paramtre et
ce, entre crochets. Elle fait une pause dune seconde ( sleep(1000)) entre la
fin de la chane et le crochet fermant.
// Programme non synchronise
class Callme {
void call(String msg) {
System.out.print("[" + msg);

Support de cours Java


try {
Thread.sleep(1000);
} catch(InterruptedException e) {
System.out.println("Interrompue");
}
System.out.println("]");
}
}// class Callme
class Caller implements Runnable {
String msg;
Callme cible;
Thread th;
public Caller(Callme cib, String ch) {
cible = cib;
msg = ch;
th = new Thread(this);
th.start();
}
public void run() {
cible.call(msg);
}
}// class Caller
class Synch {
public static void main(String args[]) {
Callme cible = new Callme();
Caller activ1 = new Caller(cible, "Bonjour");
Caller activ2 = new Caller(cible, "monde");
Caller activ1 = new Caller(cible, "synchronise");
// On attend que les activites se terminent
try {
activ1.th.join();
activ2.th.join();
activ3.th.join();
} catch(InterruptedException e) {
System.out.println("Interrompue");
}
}
}// class Synch

I.3 Synchronisation

La sortie du programme est :


[Bonjour[monde[synchronise]
]
]

En appelant sleep(), la mthode call() permet que lexcution passe


une autre thread. Ce qui donne des affichages inter-mls.
Ceci est une condition de course, les threads tant en comptition pour
arriver la fin de la mthode call().
Pour srier les accs call(), il faut restreindre son accs une thread
la fois.
Ceci se fait via le mot cl synchronized :
class Callme {
synchronized void call(String msg) {
...

La sortie du programme est alors


[Bonjour]
[monde]
[synchronise]

3.3 Instruction synchronized


Supposez vouloir synchroniser laccs aux objets dune classe qui na
pas t construite pour un accs multi-threads. En outre, vous navez pas
au code source.
Un appel synchronis peut tre ralis dans un bloc synchronis :
synchronized(objet) {
// instructions a synchroniser
}

o objet est une rfrence lobjet synchroniser.


Un bloc synchronis assure quun appel une mthode quelconque d
objet ne pourra se faire quune fois que la thread courante sera
entre dans le moniteur.
Autre version de lexemple prcdent, avec un bloc synchronis lintrieur
de la mthode run()
// Programme non synchronise
class Callme {
void call(String msg) {
System.out.print("[" + msg);
try {

10

Support de cours Java


Thread.sleep(1000);
} catch(InterruptedException e) {
System.out.println("Interrompue");
}
System.out.println("]");
}
}// class Callme
class Caller implements Runnable {
String msg;
Callme cible;
Thread th;
public Caller(Callme cib, String ch) {
cible = cib;
msg = ch;
th = new Thread(this);
th.start();
}
public void run() {
synchronized(cible) {
cible.call(msg);
}
}
}// class Caller

// bloc synchronise

class Synch {
public static void main(String args[]) {
Callme cible = new Callme();
Caller activ1 = new Caller(cible, "Bonjour");
Caller activ2 = new Caller(cible, "monde");
Caller activ1 = new Caller(cible, "synchronise");
// On attend que les activites se terminent
try {
activ1.th.join();
activ2.th.join();
activ3.th.join();
} catch(InterruptedException e) {
System.out.println("Interrompue");
}

I.3 Synchronisation

11

}
}// class Synch

3.4 Communication inter-threads


Pas besoin de scrutation entre threads. Exemple dun producteur et dun
consommateur avec la contrainte que le producteur doit attendre que le
consommateur ait termin avant quil puisse gnrer des donnes supplmentaires.
Dans un systme avec scrutation, le consommateur perd des cycles CPU
attendre que le producteur produise ; lorsque le producteur a termin, il
perd des cycles CPU attendre que le consommateur ait termin, et ainsi
de suite.
La scrutation est vite avec un mcanisme de communication inter-threads
accessible travers 3 mthodes :
La mthode wait() : fait sortir la tche appelante du moniteur et la met
en sommeil jusqu ce quune autre thread entre dans le mme moniteur
et appelle notify() ;
La mthode notify() : rveille la premire thread ayant appel wait()
sur le mme objet ;
La mthode notifyAll() : rveille toutes les threads ayant appel wait()
sur le mme objet ; la thread de priorit la plus leve sexcutera en
premier.
Ces mthodes sont implantes comme final dans Object, de sorte que
toutes les classes en disposent. Ces trois mthodes ne peuvent tre appeles
qu lintrieur dune mthode synchronise.
Leur dclaration est :
final void wait()
final void notify()
final void notifyAll()

Il y a galement la forme final void wait(long timeout) qui permet dattendre un nombre donn de millisecondes.
Exemple dimplantation incorrecte dune forme simple du problme producteur/consommateur.
Il y a 4 classes :
La classe FileAttente, la file dont on veut synchroniser les accs ;
La classe Producteur, lobjet thread qui produit les entres de la file ;
La classe Consommateur qui les consomme ;
La classe ProdCons, la classe qui cre la file, le producteur et le consommateur.

12

Support de cours Java

// Implantation incorrecte dun producteur et dun consommateur


class FileAttente {
int n;
synchronized int prendre() {
System.out.println("Pris : " + n);
return n;
}
synchronized void mettre(int n) {
this.n = n;
System.out.println("Mis : " + n);
}
}// class FileAttente
class Producteur implements Runnable {
FileAttente f;
Producteur(FileAttente f) {
this.f = f;
new Thread(this, "Producteur").start();
}
public void run() {
int i = 0;
while(true) {
f.mettre(i++);
}
}
} // class Producteur
class Consommateur implements Runnable {
FileAttente f;
Consommateur(FileAttente f) {
this.f = f;
new Thread(this, "Consommateur").sart();
}
public void run() {
while(true) {
f.prendre();

I.3 Synchronisation

13

}
}
} // class Consommateur
class ProdCons {
public static void main(String args[]) {
FileAttente f = new FileAttente();
new Producteur(f);
new Consommateur(f);
System.out.println("Ctrl-C pour arreter.");
}// class ProdCons

Bien que les mthodes put() et get() de FileAttente soient synchronises,


rien narrte ici le producteur de dpasser le consommateur et rien narrte
non plus le consommateur de consommer la mme valeur 2 fois. On pourra
ainsi obtenir lcran, par exemple
Mis : 1
Pris : 1
Pris : 1
Pris : 1
Mis : 2
Mis : 3
Mis : 4
Pris : 4

Le moyen de corriger cela est dutiliser wait() et notify() pour signaler


dans les deux directions :
// Implantation correcte dun producteur et dun consommateur
class FileAttente {
int n;
boolean valeurMise = false; // A-t-on mis une valeur dans la file ?
synchronized int prendre() {
if(valeurMise == false)
try {
wait();
} catch(InterruptedException e) {
System.out.println("InterruptedException interceptee");
}
System.out.println("Pris : " + n);
ValeurMise = false;
notify();
return n;

14

Support de cours Java


}
synchronized void mettre(int n) {
if(valeurMise == true)
try {
wait();
} catch(InterruptedException e) {
System.out.println("InterruptedException interceptee");
}
this.n = n;
ValeurMise = true;
System.out.println("Mis : " + n);
notify();
}
}// class FileAttente
class Producteur implements Runnable {
FileAttente f;
Producteur(FileAttente f) {
this.f = f;
new Thread(this, "Producteur").start();
}
public void run() {
int i = 0;
while(true) {
f.mettre(i++);
}
}
} // class Producteur
class Consommateur implements Runnable {
FileAttente f;
Consommateur(FileAttente f) {
this.f = f;
new Thread(this, "Consommateur").sart();
}
public void run() {
while(true) {

I.4 Groupes de threads

15

f.prendre();
}
}
} // class Consommateur
class ProdCons {
public static void main(String args[]) {
FileAttente f = new FileAttente();
new Producteur(f);
new Consommateur(f);
System.out.println("Ctrl-C pour arreter.");
}// class ProdCons

Et lon obtient ainsi lcran


Mis : 1
Pris : 1
Mis : 2
Pris : 2
Mis : 3
Pris : 3
Mis : 4
Pris : 4

I.4

Groupes de threads

4.1 Groupes de threads de lAPI Java


Les groupes de threads sont organises en arbre. La racine de cet arbre est
le groupe System (System thread group) ; son enfant est le groupe Main,
qui contient la thread dite Main, excutant la mthode main().
Dans le groupe System, on a 4 threads dmons :
Gestionnaire dhorloge (Clock handler) Il gre les timeouts crs par
des appels aux mthodes sleep() et repaint() (qui redessine le contenu
dune fentre).
Thread oisive (Idle thread) Cest une thread de priorit exceptionnellement basse : 0 (en fait, on ne peut fixer la priorit dune thread cette
valeur via la mthode setPriority()). Elle ne tourne donc que lorsque
toutes les autres threads sont bloques. Cela permet en fait au ramassemiettes de savoir quand il peut faire la chasse aux objets qui ne sont plus
rfrencs.

16

Support de cours Java


Ramasse-miettes (Garbage collector) Il inspecte les objets au sein de
la machine virtuelle, teste sils sont toujours rfrencs et libre la place
mmoire correspondante si ce nest pas le cas. Par dfaut, le ramassemiettes dort pendant une seconde ; lorsquil se rveille, il regarde si la
thread oisive est en cours dexcution. Si oui, il suppose que le systme
na rien faire et il commence inspecter la mmoire ; sinon, il se rendort
pendant une seconde. Le ramasse-meittes a une priorit gale 1, tout
comme la thread de finalisation.
Thread de finalisation (Finalizer thread) Elle appelle la mthode finalize()
sur les objets librs par le ramasse-miettes. Elle tourne priorit 1.
Dans le groupe Main, on a 4 threads, dont 3 ne seront actives que si
lapplication utilise des composants graphiques (paquetages du JFC
comme java.awt, etc.) :
Thread Main (Main thread) La thread par dfaut de ce groupe excute
la mthode main().
Thread AWT-Input Ne tourne que si lapplication utilise des composants graphiques. Gre les entres du systme de fentrage sous-jacent et
effectue les appels ncessaires aux mthodes de gestion dvnements de
lAWT (Abstract Window Toolkit).
Thread AWT-Toolkit Gre les vnements du systme de fentrage sousjacent et appelle les mthodes appropries. Cette thread est, dans AWT
1.1, spcifique la plateforme et se nomme AWT-Motif (sur UNIX),
AWT-Windows, etc. Certaines plateformes utilisent deux threads au lieu
dune.
Rafrachisseur dcran (ScreenUpdater) Gre les appels la mthode
repaint(). Lorsquun tel appel se produit, cette thread est prvenue et
elle appelle les mthodes update() des composants concerns.

4.2 Groupe de threads ThreadGroup


La classe ThreadGroup permet de crer un groupe de threads.
2 constructeurs :
Avec un paramtre : ThreadGroup(String groupName)
Avec deux : ThreadGroup(ThreadGroup parentObj, String groupName)
Dans les 2 formes, groupName spcifie le nom du groupe de threads. La
premire version cre un nouveau groupe qui a la thread courante comme
parent. Dans le 2 ime forme, le parent est spcifi par parentObj.
Mthodes de ThreadGroup :
methode()

But

I.4 Groupes de threads


static int activeCount()

static int activeCountGroup()


void checkAccess()

void destroy()
static int enumerate(Thread
group[])
static int enumerate(Thread
group[], boolean all)

static int enumerate(ThreadGroup


group[])
static int enumerate(ThreadGroup
group[], boolean all)

final int getMaxPriority()


final String getName()
final ThreadGroup getParent()

final boolean isDaemon()


void list()
final boolean parentOf(ThreadGroup
group)
final void resume()
final void setDaemon(boolean
isDaemon)

17
Renvoie le nombre de threads dans le
groupe, plus le nombre de groupe dont
cette thread est le parent.
Renvoie le nombre de groupe dont cette
thread est le parent.
Provoque la vrification par le gestionnaire
de scurit du fait que la thread appelante
peut eccder et/ou changer le groupe partir duquel checkAccess() est appel.
Dtruit le groupe de threads (et tous les
enfants) partir duquel lappel est effectu.
Les threads constituant le groupe appelant
sont stockes dans le tableau group.
Les threads constituant le groupe appelant
sont stockes dans le tableau group. Si all
est true, les threads de tous les sous
groupes de la thread appelante sont galement stocks dans group.
Les threads constituant le groupe appelant
sont stockes dans le tableau group.
Les threads constituant le groupe appelant sont stockes dans le tableau group.
Si all est true, les sous groupes de
tous les sous groupes (et ainsi de suite) de
la thread appelante sont galement stocks
dans group.
Renvoie la plus haute priorit dun membre
du groupe.
Renvoie le nom du groupe.
Renvoie null si le ThreadGroup appelant
na pas de parent. Sinon, renvoie le parent
de lobjet invoqu.
Renvoie true si le groupe est un groupe
de dmons. Sinon, renvoie false.
Affiche des informations sur le groupe.
Renvoie true si la thread appelante est
le parent de group (ou group lui-mme).
Sinon, renvoie false.
Fait reprendre lexcution toutes les
threads du groupe appelant.
Si isDaemon est gal true, le groupe
appelant est tiquet comme un groupe de
dmons.

18

Support de cours Java

final void setMaxPriority(int


priority)
final void stop()
final void suspend()
String toString()
void uncaughtException(Thread
thread, Throwable e)

Fixe la priorit maximale du groupe appelant priority.


Termine toutes les threads du groupe appelant.
Suspend toutes les threads du groupe appelant.
Renvoie un quivalent String du groupe
appelant.
Cette mthode est appele lorsquune excpetion nest pas leve.

4.3 Exemple de groupe de threads


Lexemple suivant liste (mthode listAllThreads() les threads (et leurs
groupes) sexcutant.
La mthode listAllThread() utilise currentThread() (de la classe Thread)
pour obtenir la thread courante, et utilise getThreadGroup() pour trouver
le groupe de cette thread.
Elle utilise ensuite getParent() de ThreadGroup pour se dplacer dans
la hirarchie des groupes de threads jusqu trouver le groupe racine, qui
contient tous les autres groupes.
La mthode listAllThreads() appelle ensuite la mthode prive list_group()
de ThreadLister pour afficher le contenu du groupe de threads racine, et
pour afficher rcursivement le contenu de tous les groupes quelle contient.
La mthode list_group(), et la mthode print_thread_info() quelle
appelle, utilisent diverses mthodes de Thread et de ThreadGroup pour
obtenir des informations sur les threads et sur leurs groupes.
Remarquez que la mthode isDaemon() dtermine si une mthode est un
dmon ou pas. Une thread dmon est cense sexcuter en arrire-plan et
ne pas se terminer. Linterprteur Java termine lorsque toutes les threads
non dmon ont termin.
import java.io.*;
import java.applet.*;
import java.awt.*;

class ThreadLister {
// Afficher des informations a propos dune activite
private static void print_thread_info(PrintStream out, Thread t,

I.4 Groupes de threads

19

String indent) {
if (t == null) return;
out.println(indent + "Activite : " + t.getName() +
" Priorite : " + t.getPriority() +
(t.isDaemon()?" Demon":"") +
(t.isAlive()?"":" Non vivante"));
}
// Afficher des informations a propos dun groupe dactivite, de
//
ses activites et groupes
private static void list_group(PrintStream out, ThreadGroup g,
String indent) {
if (g == null) return;
int num_threads = g.activeCount();
int num_groups = g.activeGroupCount();
Thread[] threads = new Thread[num_threads];
ThreadGroup[] groups = new ThreadGroup[num_groups];
g.enumerate(threads, false);
g.enumerate(groups, false);
out.println(indent + "Groupe dactivites : " + g.getName() +
" Priorite max : " + g.getMaxPriority() +
(g.isDaemon()?" Demon":""));
for(int i = 0; i < num_threads; i++)
print_thread_info(out, threads[i], indent + "
for(int i = 0; i < num_groups; i++)
list_group(out, groups[i], indent + "
");

");

}
// Trouver la racine du groupe dactivites et lister recursivement
public static void listAllThreads(PrintStream out) {
ThreadGroup current_thread_group;
ThreadGroup root_thread_group;
ThreadGroup parent;
// Obtenir le groupe dactivites courant
current_thread_group = Thread.currentThread().getThreadGroup();
// Aller chercher la racine des groupes dactivites
root_thread_group = current_thread_group;
parent = root_thread_group.getParent();

20

Support de cours Java


while(parent != null) {
root_thread_group = parent;
parent = parent.getParent();
}
// Et le lister, recursivement
list_group(out, root_thread_group, "");
}

public static void main(String[] args) {


ThreadLister.listAllThreads(System.out);
}
}// class ThreadLister

public class AppletThreadLister extends Applet {


TextArea textarea;
// Creer une zone de texte pour y mettre les informations
public void init() {
textarea = new TextArea(20, 60);
this.add(textarea);
Dimension prefsize = textarea.preferredSize();
this.resize(prefsize.width, prefsize.height);
}
// Effectuer laffichage. ByteArrayOutputStream est bien utile
public void start() {
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(os);
ThreadLister.listAllThreads(ps);
textarea.setText(os.toString());
}
}

I.5

tats dune thread et ordonnancement

5.1 Descriptif des tats dune thread


Une thread de Java peut se trouver dans lun des 7 tats qui suivent.

I.5 tats dune thread et ordonnancement


nouveau (new)

21

Lorsquune nouvelle thread est cre, par exemple avec

Thread th = new Thread(a);

prt (runnable) ou prte sexcuter. Lorsque la mthode start() dune


thread est appele, cette dernire passe dans ltat prt. Toutes les threads
prtes dun programme Java sont organises par la machine virtuelle en une
structure de donnes nomme la file dattente prte. Une thread entrant
dans ltat prt est mise dans cette file. Le code de la mthode run() sera
appel lexcution de la thread.
en cours dexcution (running) La thread se voit alloue des cycles CPU
par lordonnanceur. Si une thread dans ltat en cours dexcution appelle sa
mthode yield(), ou si elle est supplante par une thread de priorit plus
leve, elle laisse la CPU et est remise dans la file dattente prte, entrant
dans ltat prt.
suspendu (suspended) Une thread prte ou en cours dexcution entre
dans ltat suspendu lorsque sa mthode suspend() est appele. Une thread
peut se suspendre elle-mme ou tre par une autre. De ltat suspendu, une
thread r-entre dans ltat prt lorsque sa mthode resume() est appele
par une autre thread.
bloqu (blocked) Une thread entre dans ltat bloqu lorsque
elle appelle sa mthode sleep(),
elle appelle wait() dans une mthode synchronise dun objet,
elle appelle join() sur un autre objet dont la thread ne sest pas encore
termine,
elle effectue une opration dentre/sortie bloquante (comme lire partir
du clavier).
partir de ltat bloqu, une thread r-entre dans ltat prt.
suspendu-bloqu (suspended-blocked) Losquune thread est bloque est
suspendue par une autre thread. Si lopration bloquante se termine, la
thread entre dans ltat suspendu. Si la thread est rintgre (resumed)
avant que lopration bloquante ne se termine, elle entre dans ltat bloqu.
mort (dead) Une thread se termine et entre dans ltat mort lorsque sa
mthode run() se termine ou lorsque sa mthode stop() est appele.

5.2 Rcapitulatif des tats dune thread


Le tableau suivant rsume les diffrents tats et les vnements qui engendrent
une transition dun tat un autre

22

Support de cours Java


Nouvel tat

tat
actuel

prt

nouveau
prt

start()

en cours2

quantum

puis,

en
cours

suspendu bloqu

lu1

suspend()

suspend()

yield()

suspendu- mort
bloqu
stop()

sur E/S,
sleep(),
wait(),

stop()
run()

termin

join()

suspendu

bloqu

resume()

stop()

E/S termine,
sleep() expire,
notify(),
notifyAll(),

suspend()

stop()

join()

termin

suspendubloqu

E/S termine

resume()

stop()

5.3 Ordonnancement
Lordonnaceur sassure que la thread de plus haute priorit (ou les sil y en
a plusieurs dgale priorit) est excute par la CPU.
Si une thread de plus haute priorit entre dans la file des threads prtes,
lordonnanceur de threads Java met la thread en cours dans la file dattente,
pour que la prioritaire sexcute.
La thread en cours est dite supplante par premption par la thread de plus
haute priorit.
Si la thread en cours dexcution passe la main (via yield(), est suspendue
ou bloque, lordonnanceur choisit dans la file dattente prte la thread de
priorit la plus leve (ou lune dentre elles si elles sont plusieurs) pour tre
excute.
Un autre aspect est le partage du temps (time slicing). Un ordre de grandeur
courant pour un quantum de temps est 100ms. En temps partag, si une
thread en cours dexcution na pas t stoppe, bloque, suspendue, ne
sest pas termine, ou na pas pass la main, et sil y a dautres threads
de priorit gale dans la file dattente prte, la CPU est ralloue lune
dentre elles.
Sous Solaris 2.x, la version 1.1 du JDK neffectue pas de partage de temps
entre les threads : une thread sexcute jusqu ce quelle se termine,
1
2

(par lordonnanceur)
(dexcution)

I.5 tats dune thread et ordonnancement

23

soit stoppe, suspendue, bloque, passe la main ou quune autre


thread de plus haute priorit devienne prte.
Le JDK 1.1 pour Windows 95/NT partage le temps entre les threads.

5.4 Rsum pour la cration dune thread


Les constructeurs dune Thread acceptent comme arguments :
Un objet Runnable, auquel cas un start() ultrieur appelle run() de
lobjet Runnable fourni. Si aucun Runnable nest fourni, limplantation
par dfaut de run() retourne immdiatement.
Une String servant didentificateur de la thread. Nest utile que pour le
traage ou le debugging.
Le ThreadGroup dans laquelle la nouvelle Thread doit tre place. Si
laccs au ThreadGroup nest pas autoris, une SecurityException est
leve.

II Gnie logiciel appliqu


Rfrences bibliographiques

II.1

Object-Oriented Analysis and Design with Applications, G. Booch [Boo94],


Flexible Java, B. Venners [Ven99],
Design Patterns, E. Gamma, R. Helm, R. Johnson et J. Vlissides [GHJV95],
Data Structures, M.A. Weiss [Wei98].

Implantation dune pile en Java

1.1 Implantation Java dune pile : interface


Nous avons besoin (par souci de commodit) dune exception de type Underflow
que lon dfinit per exemple comme suit :
/**
* Classe Exception pour lacces dans des conteneurs vides
* tels des piles, listes et files dattente avec priorite
*/
public class Underflow extends Exception
{
/**
* Construit cet objet exception
* @param message le message derreur.
*/
public Underflow( String message )
{
super( message );
}
}

Voici linterface dune pile


25

26

Support de cours Java

package DataStructures;
import Exceptions.*;
//
//
//
//
//
//
//
//
//
//
//

Stack interface
****************** OPERATIONS PUBLIQUES *************************
void push( x )
--> Inserer x
void pop( )
--> Enlever lelt insere le + recemment
Object top( )
--> Renvoyer lelt insere le + recemment
Object topAndPop( )
--> Renvoyer et enlever lelt le + receent
boolean isEmpty( )
--> Renvoyer true si vide; false sinon
void makeEmpty( )
--> Enlever tous les elements
****************** ERREURS **************************************
top, pop, ou topAndPop sur une pile vide

/**
* Protocole pour piles
*/
public interface Stack
{
/**
* Tester si la pile est logiquement vide
* @return true si vide, false sinon.
*/
boolean isEmpty( );
/**
* Prendre lelement insere le plus recemment sur la pile.
* naltere pas la pile.
* @return lelement le plus recemment insere dans la pile
* @exception Underflow si la pile est vide
*/
Object top( )
throws Underflow;
/**
* Enleve lelement insere le plus recemment sur la pile
* @exception Underflow si la pile est vide
*/
void
pop( )
throws Underflow;
/**

II.1 Implantation dune pile en Java

27

* Renvoie et enleve lelement le plus recemment insere


* de la pile.
* @return lelement le plus recemment insere dans la pile
* @exception Underflow si la pile est vide
*/
Object topAndPop( ) throws Underflow;
/**
* Insere un nouvel element sur la pile
* @param x lelement a inserer.
*/
void
push( Object x );
/**
* Rendre la pile logiquement vide
*/
void
makeEmpty( );
}

1.2 Implantation Java dune pile par tableau


Voici maintenant limplantation par tableau dune pile. Notez que le tableau
crot de manire dynamique.
package DataStructures;
import Exceptions.*;

// StackAr class
//
// CONSTRUCTION : sans constructeur
//
//
//
//
//
//
//
//
//
//

****************** OPERATIONS PUBLIQUES *************************


void push( x )
--> Inserer x
void pop( )
--> Enlever lelt insere le + recemment
Object top( )
--> Renvoyer lelt insere le + recemment
Object topAndPop( )
--> Renvoyer et enlever lelt le + receent
boolean isEmpty( )
--> Renvoyer true si vide; false sinon
void makeEmpty( )
--> Enlever tous les elements
****************** ERREURS **************************************
top, pop, ou topAndPop sur une pile vide

28

Support de cours Java

/**
* Implantation dune pile a partir dun tableau
*/
public class StackAr implements Stack
{
/**
* Construire la pile
*/
public StackAr( )
{
theArray = new Object[ DEFAULT_CAPACITY ];
topOfStack = -1;
}

public boolean isEmpty( )


{
return topOfStack == -1;
}

public void makeEmpty( )


{
topOfStack = -1;
}

public Object top( ) throws Underflow


{
if( isEmpty( ) )
throw new Underflow( "Stack top" );
return theArray[ topOfStack ];
}

public void pop( ) throws Underflow


{
if( isEmpty( ) )
throw new Underflow( "Stack pop" );
topOfStack--;
}

II.1 Implantation dune pile en Java


public Object topAndPop( ) throws Underflow
{
if( isEmpty( ) )
throw new Underflow( "Stack topAndPop" );
return theArray[ topOfStack-- ];
}
public void push( Object x )
{
if( topOfStack + 1 == theArray.length )
doubleArray( );
theArray[ ++topOfStack ] = x;
}
/**
* Methode interne pour etendre theArray.
*/
private void doubleArray( )
{
Object [ ] newArray;
newArray = new Object[ theArray.length * 2 ];
for( int i = 0; i < theArray.length; i++ )
newArray[ i ] = theArray[ i ];
theArray = newArray;
}
private Object [ ] theArray;
private int
topOfStack;
static final int DEFAULT_CAPACITY = 10;
}

1.3 Implantation Java dune pile par liste


Nous avons dabord besoin dlments dune liste
// Moeud de base stocke dans une liste chainee
class ListNode
{
// Constructeurs
ListNode( Object theElement )
{

29

30

Support de cours Java


this( theElement, null );
}
ListNode( Object theElement, ListNode n )
{
element = theElement;
next
= n;
}
// Donnees accessibles par dautres classes du meme paquetage
Object
element;
ListNode next;

Voici maintenant limplantation par liste dune pile


package DataStructures;
import Exceptions.*;
// StackLi class
//
//
// CONSTRUCTION : sans initialiseur
//
//
//
//
//
//
//
//
//
//

****************** OPERATIONS PUBLIQUES *************************


void push( x )
--> Inserer x
void pop( )
--> Enlever lelt insere le + recemment
Object top( )
--> Renvoyer lelt insere le + recemment
Object topAndPop( )
--> Renvoyer et enlever lelt le + receent
boolean isEmpty( )
--> Renvoyer true si vide; false sinon
void makeEmpty( )
--> Enlever tous les elements
****************** ERREURS **************************************
top, pop, ou topAndPop sur une pile vide

/**
* Implantation dune pile sur une liste
*/
public class StackLi implements Stack
{
/**
* Construire la pile

II.1 Implantation dune pile en Java


*/
public StackLi( )
{
topOfStack = null;
}

public boolean isEmpty( )


{
return topOfStack == null;
}
public void makeEmpty( )
{
topOfStack = null;
}

public Object top( ) throws Underflow


{
if( isEmpty( ) )
throw new Underflow( "Stack top" );
return topOfStack.element;
}

public void pop( ) throws Underflow


{
if( isEmpty( ) )
throw new Underflow( "Stack pop" );
topOfStack = topOfStack.next;
}

public Object topAndPop( ) throws Underflow


{
if( isEmpty( ) )
throw new Underflow( "Stack topAndPop" );
Object topItem = topOfStack.element;
topOfStack = topOfStack.next;
return topItem;
}

31

32

Support de cours Java

public void push( Object x )


{
topOfStack = new ListNode( x, topOfStack );
}
private ListNode topOfStack;
}

1.4 Gnie logiciel pratique et POO


Nous allons maintenant dtailler diverses techniques
Minimiser le couplage entre mthodes
Maximiser la cohsion
Une rgle gnrale qui ne pourra quaugmenter la fiabilit : Ne faites pas aux
autres programmeurs (qui vont devoir maintenir votre code) ce que
vous naimeriez pas que lon vous fasse (si vous deviez maintenir leur
code)

II.2

Minimisation de couplage entre mthodes

2.1 Types de mthodes


Pour raliser le but vis, dtaillons trois types de mthodes en java :
Les mthodes utilitaires
Les mthodes de consultation dtat
Les mthodes de modification dtat
Ltat dune classe sera reprsent par ses variables dinstance

2.2 Mthodes utilitaires


Ce type de mthode nutilise ni ne modifie ltat de lobjet. Par exemples :
Dans la classe Integer, public static int toString(int i) : renvoie un
nouvel objet String reprsentant lentier spcifi en base 10.
Dans la classe Math, public static native double cos(double a) : envoie
le cosinus dun angle.

II.2 Minimisation de couplage entre mthodes

33

2.3 Mthode de consultation dtat


Ce type de mthode renvoie une vue de ltat de lobjet sans le modifier. Par
exemple :
Dans la classe Object, public String toString() : renvoie une reprsentation sous forme de chane de caractres dun objet.
Dans la classe Integer, public byte byteValue() : renvoie la valeur dun
Integer sous forme dun octet.
Dans la classe String, public int indexOf(int ch) : renvoie lindice dans
la chane de la premire occurence du caractre spcifi.

2.4 Mthode de modification dtat


Ce type de mthode est susceptible de modifier ltat de lobjet Exemples :
Dans la classe StringBuffer, public StringBuffer append(int i) : ajoute
la reprsentation sous forme de chane de l int au StringBuffer.
Dans la classe Hashtable, public synchronized void clear() : vide la
table de hachage, de faon quelle ne contienne plus de cls.
Dans la classe Vector, public final synchronized void addElement(Object
obj) : ajoute le composant spcifi la fin du Vector, augmentant sa taille
dune unit.

2.5 Minimisation de couplage entre mthodes


Le couplage entre mthodes est le degr dinterdpendance entre une mthode et son environnement (dautres mthodes, objets et classes).
Plus le degr de couplage est faible plus une mthode est indpendante et
plus il est facile de la modifier (plus elle est flexible)
Une technique simple pour estimer le degr de couplage est de voir une
mthode comme un transformateur de donnes avec des donnes en
entre et des donnes en sortie (une vue systmique)
entre

sortie
transformerDonnees()

Fig. II.1: Une mthode comme transformateur de donnes

2.6 Entres et sorties


Une mthode peut prendre ses entres de diverses sources :
De paramtres qui lui sont passs

34

Support de cours Java

De donnes provenant de variables de classe (de sa propre classe ou dune


autre)
Sil sagit dune mthode dinstance, de donnes provenant de variables
dinstance de lobjet appelant
Une mthode peut avoir diverses sorties
Elle peut renvoyer une valeur, soit un type primitif soit une rfrence un
objet
Elle peut modifier des objets dont on a pass une rfrence en paramtre
Elle peut modifier des variables de classe (de sa propre classe ou dune autre)
Sil sagit dune mthode dinstance, elle peut altrer les variables dinstance
de lobjet appelant
Elle peut lever une exception

2.7 Mthode utilitaire minimalement couple


Une mthode la moins couple possible en Java : mthodes utilitaires qui
Ne prend des entres que de ses paramtres
Neffectue des sorties quau travers ses paramtres ou de sa valeur de retour
(ou en levant une exception)
Naccepte en entre que des donnes rellement ncessaires la mthode
Ne renvoie comme sortie que des donnes effectivement produites par la
mthode

2.8 Exemple de bonne mthode utilitaire


Voici par exemple la mthode convertOzToMl() qui accepte un int comme
seule entre et ne renvoie comme seule sortie quun int :
class Liquid {
private static final double FL_OUNCES_PER_ML = 12.0/355.0;
private static final double ML_PER_FL_OUNCE = 355.0/12.0;
/**
* Convertit des fluid ounces en millilitres
*/
public static int convertOzToMl(int ounces) {
double d = ounces * ML_PER_FL_OUNCE;
d += 0.5;
// On doit ajouter .5 car (int) tronque
return (int) d; // Resultat tronque si la fraction est >= .5
}
}

II.2 Minimisation de couplage entre mthodes

35

En crivant une mthode utilitaire, on doit essayer de ne prendre des entres que
des paramtres et neffectuer de sorties quau travers de paramtres, de valeurs
de retour ou dexceptions.

ounces

valeur de retour

convertOzToMl()

Fig. II.2: Une bonne mthode utilitaire (minimalement couple)

2.9 Exemple de mauvaise mthode utilitaire


Un mauvais exemple qui accrot le couplage et davoir des donnes en entre
ou sortie qui ne sont pas vitales la mthode.
Par exemple, supposons quen crivant convertOzToMl(), vous pensez toujours
placer sa sortie dans un objet CoffeeCup :
class Example2 {
public static void main(String[] args) {
CoffeeCup cup = new CoffeeCup();
int amount = Liquid.convertOzToMl(16);
cup.add(amount);
//...
}
}

Si cest le cas, vous pourriez tre tent dcrire convertOzToMl() comme suit :
class Liquid {
private static final double FL_OUNCES_PER_ML = 12.0/355.0;
private static final double ML_PER_FL_OUNCE = 355.0/12.0;

public static void convertOzToMl(int ounces, CoffeeCup cup) {


double d = ounces * ML_PER_FL_OUNCE;

36

Support de cours Java


d += 0.5;
cup.add((int) d);
}

Et lutilisation que vous en feriez serait alors :


class Example3 {
public static void main(String[] args) {
CoffeeCup cup = new CoffeeCup();
Liquid.convertOzToMl(16, cup);
//...
}
}

Mais, maintenant, convertOzToMl() est couple la classe CoffeCup. Si quelquun


veut plus tard convertir des onces en millilitres sans se servir dune tasse de caf,
il lui faudra rcrire la mthode, crire une mthode diffrente ou crer un objet
CoffeCup juste pour contenir la sortie.
cup

amount

cup

convertOzToMl()

Fig. II.3: Une mauvaise mthode utilitaire

2.10 Exemple horrible


Un exemple horrible qui utilise en entre comme en sortie des variables public
static (lquivalent de variables globales du C ++)
class Liquid {
private static final double FL_OUNCES_PER_ML = 12.0/355.0;
private static final double ML_PER_FL_OUNCE = 355.0/12.0;

public static void convertOzToMl() {

II.2 Minimisation de couplage entre mthodes

37

double d = PurpleZebra.k * ML_PER_FL_OUNCE;


d += 0.5;
FlyingSaucer.q = (int) d;
}
}
class FlyingSaucer {
public static int q;
//...
}
class PurpleZebra {
public static int k;
//...
}

Pour utiliser cette version il faut :


class Example4 {
public static void main(String[] args) {
PurpleZebra.k = 16;
Liquid.convertOzToMl();
int mlFor16Oz = FlyingSaucer.q;
System.out.println("Ml for 16 oz is: " + mlFor16Oz);
}
}

Lutilisation de cette version ncessite de connatre pas mal de dtails internes dimplantation de la mthode.
Par contraste, dans une bonne mthode utilitaire, le programmeur qui sen
sert peut comprendre comment lutiliser simplement en regardant la
signature de la mthode et son type de retour (ce qui est spcifi
dans une interface).
De plus, la variable q, si elle est utilise ailleurs, peut provoquer des effets
de bords trs gnants.

2.11 Mthodes de consultation dtat minimalement


couples
Une mthode de consultation de ltat minimalement couple

38

Support de cours Java


Ne prend en entre que ses paramtres, les variables de classe (de la classe
contenant la mthode), plus (sil sagit dune mthode dinstance) les variables dinstance de lobjet appelant
Neffectue ses sorties quau travers de sa valeur de retour, de ses paramtres,
ou en levant une exception
Ceci peut tre reformul en disant : lentre dune mthode de consultation
dtat minimalement couple peut venir de nimporte o, except directement de variables de classe non constantes dclares dans dautres classes.

2.12 Mthodes de modification dtat minimalement


couples
Ne prend en entre que ses paramtres, les variables de classe (de la classe
contenant la mthode) et (sil sagit dune mthode dinstance) les variables
dinstance de lobjet appelant
Neffectue ses sorties quau travers de sa valeur de retour, de ses paramtres,
en levant une exception, ou (sil sagit dune mthode dinstance) au travers
des variables dinstance de lobjet appelant
Ceci peut tre reformul en disant : lentre dune mthode de modification
dtat minimalement couple peut venir de nimporte o, except directement de variables de classe non constantes dclares dans dautres classes.
La sortie dune mthode de modification dtat minimalement couple peut
sexprimer de nimporte quelle faon, except en modifiant directement des
donnes dclares ou rfrences partir de variables de classes dclares
dans dautres classes.
La mthode add() de CoffeCup en est un exemple :
class CoffeeCup {
private int innerCoffee;
public void add(int amount) {
innerCoffee += amount;
}
//...
}

2.13 Remarque sur la rfrence cache this


Les mthodes dinstance peuvent accder aux variables dinstance, car elles
reoivent une rfrence cache this comme premier paramtre

II.3 Maximisation de cohsion des mthodes

39

Le compilateur Java insre une rfrence this au dbut de la liste des


paramtres de toute mthode dinstance quil compile
Cette rfrence cache this nest pas passe aux mthodes de classe (dclares static) ; cest pourquoi lon na pas besoin dinstance pour les invoquer
et pourquoi elles ne peuvent accder aux variables dinstances

2.14 Directives de couplage minimal


Directives pour les champs Ne pas assigner de signification spciale
des valeurs spcifiques dun champ en reprsentant plusieurs attributs
au sein du mme champ. Utiliser au lieu de cela un champ spar pour
reprsenter chaque attribut dune classe ou dun objet.
Le corollaire constant Prfrer des constantes (champs static final)
des littraux cods en dur ( 1.5, "Coucou !"), particulirement si la mme
constante doit tre utilise en plusieurs endroits.
Le mantra de la minimisation de couplage Toujours essayer de minimiser le couplage des mthodes : ne prendre comme entres que les donnes
ncessaires la mthode ; neffectuer comme sortie que les donnes produites
par la mthode.

II.3

Maximisation de cohsion des mthodes

3.1 Notion de cohsion


Les mthodes effectuent des oprations.
bas niveau, ce sont des oprations comme accepter des donnes en
entre, oprer sur les donnes et dliver des donnes en sortie
haut niveau, cela sera cloner cet objet, ajouter cet lment la fin
de ce vecteur, voir tel enregistrement de la base de donnes des clients
Minimiser le couplage ncessite une vision bas niveau des mthodes
Le couplage mesure comment les entres et les sorties dune mthode sont
connectes dautres parties du programme
Par contraste, maximiser la cohsion ncessite de regarder les mthodes
un haut niveau
La cohsion mesure le degr avec lequel une mthode effectue tche conceptuelle donne.
Plus une mthode est concentre sur laccomplissement dune seule tche
conceptuelle, plus elle est cohsive

40

Support de cours Java

3.2 Pourquoi maximiser la cohsion ?


Plus vos mthodes seront cohsives, plus votre code sera flexible (facile
comprendre et modifier). Elles seront alors lanalogue de briques de base
en LEGO.
Les mthodes cohsives aident la flexibilit sous deux angles :
1/ Si votre code est plus focalis sur une seule tche conceptuelle, il est plus
facile de lui trouver un nom qui indique clairement ce que la mthode fait.
Par exemple, une mthode nomme convertOzToMl(int ounces) est plus
facile comprendre que
convert(int fromUnits, int toUnits, int fromAmount)

2/ Les mthodes cohsives rendent votre code plus facile changer lorsque
chaque mthode effectue une seule tche bien dfinie. On dispose de
briques de base lmentaires laide desquelles on peut contruire telle
ou telle architecture logicielle. De plus, les changements de comportement que lon peut tre amen faire au sein dune mthode seront plus
isols si ce comportement est emball dans sa propre mthode

3.3 Exemple de mthode peu cohsive


Voici un exemple de mthode peu cohsive sur les tasses caf
public class CoffeeCup {
public final static int ADD = 0;
public final static int RELEASE_SIP = 1;
public final static int SPILL = 2;
private int innerCoffee;
public int modify(int action, int amount) {
int returnValue = 0;
switch (action) {
case ADD:
// ajouter des doses de cafe
innerCoffee += amount;
// renvoyer zero, meme si cest sans signification
break;
case RELEASE_SIP:

II.3 Maximisation de cohsion des mthodes

41

// enlever les doses de caffe passees avec amount


int sip = amount;
if (innerCoffee < amount) {
sip = innerCoffee;
}
innerCoffee -= sip;
// renvoyer ce qui a ete enleve
returnValue = sip;
break;
case SPILL:
// mettre innerCoffee a 0
// ignorer le parametre amount
int all = innerCoffee;
innerCoffee = 0;
// renvoyer tout le cafe
returnValue = all;
default:
// Il faut ici lever une exception, car une
// commande invalide a ete passee
break;
}
return returnValue;
}
}

La mthode modify() est peu cohsive parce quelle inclus du code pour
effectuer des actions qui sont conceptuellement assez diffrentes
Elle est difficile comprendre en partie parce que son nom, modify(), nest
pas trs spcialis
Une autre raison pour laquelle elle est difficile comprendre est que certaines
donnes qui lui sont passes ou quelle renvoie ne sont utilises que dans
certains cas.
Par exemple, si action est gal CoffeCup.ADD, la valeur de retour de
la mthode est sans signification
De mme, si si action est gal CoffeCup.SPILL, le paramtre dentre
amount est inutilis.
Si lon ne regarde qu la signature de la mthode et sa valeur de retour,
il nest pas clair de deviner ce que la mthode va faire.

42

Support de cours Java


Ce type de mthode est dcrit par la figure II.4
amount

action

valeur de retour

modify()

Fig. II.4: Passage de contrle modify()


Le disque du paramtre action est noir, ce qui indique que ce paramtre
contient des donnes utilises pour du contrle
Les mthodes traitent des donnes fournies en entre et gnrent des donnes en sortie. Lorsquune mthode utilise des donnes dentre non pour le
traitement, mais pour dcider comment traiter, ces donnes sont utilises
pour du contrle.
Afin de maximiser la cohsion, vitez de passer des donnes de contrle
des mthodes ; au lieu de cela, essayez de diviser les fonctionnalits de la
mthode en plusieurs mthodes qui nont pas besoin de donnes de contrle.
Il est par contre tout fait indiqu de renvoyer des donnes de contle
dune mthode. Lever une exception en est un bon exemple. Les donnes de
contrle devraient tre passes dune mthode celle qui la appele.

3.4 Exemple de mthode moyennement cohsive


Afin de rendre modify() plus cohsive, scindons-la en deux : add() et
remove()
public class CoffeeCup {
private int innerCoffee;
public void add(int amount) {
innerCoffee += amount;
}
public int remove(boolean all, int amount) {
int returnValue = 0;

II.3 Maximisation de cohsion des mthodes

43

if (all) {
int allCoffee = innerCoffee;
innerCoffee = 0;
returnValue = allCoffee;
}
else {
// remove the amount of coffee passed as amount
int sip = amount;
if (innerCoffee < amount) {
sip = innerCoffee;
}
innerCoffee -= sip;
returnValue = sip;
}
return returnValue;
}
}

Cette solution est meilleure, mais pas encore optimale. La mthode add()
na pas besoin de paramtres de contrle, mais remove() en a besoin.
amount

valeur de retour

all

remove()

Fig. II.5: Passage de contrle modify()

3.5 Exemple de mthode hautement cohsive


Afin dobtenir un meilleure conception, divisons remove() en deux mthodes
supplmentaires, aucune des deux nacceptant des donnes de contrle ou
ayant des paramtres qui sont utiliss seulement une partie du temps. On
divise donc en releaseOneSip() et spillEntireContents()
class CoffeeCup {
private int innerCoffee;

44

Support de cours Java


public void add(int amount) {
innerCoffee += amount;
}
public int releaseOneSip(int sipSize) {
int sip = sipSize;
if (innerCoffee < sipSize) {
sip = innerCoffee;
}
innerCoffee -= sip;
return sip;
}
public int spillEntireContents() {
int all = innerCoffee;
innerCoffee = 0;
return all;
}
}

Donc, au lieu dexprimer ses souhaits au travers dun paramtre supplmentaire, on appelle une mthode supplmentaire. Par exemple, au lieu de
faire
class Example1 {
public static void main(String[] args) {
CoffeeCup cup = new CoffeeCup();
// ignorer la valeur de retour erronnee de modify()
//
dans le case ADD
cup.modify(CoffeeCup.ADD, 355);
int mlCoffee = cup.modify(CoffeeCup.RELEASE_SIP, 25);
// 2ieme parametre inutilise dans le case SPILL
mlCoffee += cup.modify(CoffeeCup.SPILL, 0);
System.out.println("Ml of coffee: " + mlCoffee);
}
}

on fera
class Example3 {
public static void main(String[] args) {

II.3 Maximisation de cohsion des mthodes

45

CoffeeCup cup = new CoffeeCup();


cup.add(355);
int mlCoffee = cup.releaseOneSip(25);
mlCoffee += cup.spillEntireContents();
System.out.println("Ml of coffee: " + mlCoffee);
}
}

Ce code est plus facile comprendre, chaque mthode tant responsable


dune tche bien dtermine.
Il est de plus assez flexible, parce quil est plus ais de changer une fonctionnalit sans affecter les autres

3.6 Suppositions rduites


Les mthodes fonctionnellement cohsives augmentent la flexibilit du code
galement parce quelle font moins de suppositions sur lordre dans lequel
les actions particulires sont effectues.
Voici un exemple fonctionnellement peu cohsif, parce quil fait trop de
suppositions
class CoffeeCup {
private int innerCoffee;
private int innerCream;
private int innerSugar;
private static final int CREAM_FRACTION = 30;
private static final int SUGAR_FRACTION = 30;
public void add(int amountOfCoffee) {
innerCoffee += amountOfCoffee;
innerCream += amountOfCoffee/CREAM_FRACTION;
innerSugar += amountOfCoffee/SUGAR_FRACTION;
}
//...
}

La mthode add() augmente bien la quantit de caf innerCoffee, mais


suppose galement que lutilisateur voudra du sucre et de la crme, en quantit relative la quantit de caf.
Une conception plus flexible est

46

Support de cours Java


class CoffeeCup {
private int innerCoffee;
private int innerCream;
private int innerSugar;
public void addCoffee(int amount) {
innerCoffee += amount;
}
public void addCream(int amount) {
innerCream += amount;
}
public void addSugar(int amount) {
innerSugar += amount;
}
//...
}

3.7 La cohsion est haut niveau


La cohsion reprsente le fait deffectuer une opration haut niveau, bien
qu bas niveau, il puisse y avoir pas mal doprations effectues.
Par exemple, supposons avoir un consommateur rgulier, Joe, qui veut toujours son caf avec 30 doses de caf, une dose de crme et une dose de sucre.
Ceci peut simplanter comme suit
class VirtualCafe {
private static final int JOES_CREAM_FRACTION = 30;
private static final int JOES_SUGAR_FRACTION = 30;
public static void prepareCupForJoe(CoffeeCup cup, int amount) {
cup.addCoffee(amount);
cup.addCream(amount/JOES_CREAM_FRACTION);
cup.addSugar(amount/JOES_SUGAR_FRACTION);
}
}

La mthode prepareCupForJoe() est fonctionnellement cohsive, bien qu


bas niveau, elle effectue les mmes actions que la prcdente mthode add(),
qui ntait pas cohsive.

II.3 Maximisation de cohsion des mthodes

47

La raison est que, conceptuellement, prepareCupForJoe() prpare du caf


pour Joe, et pas pour tout le monde, comme le faisit add(). Elle nempche donc pas dautres mthodes de se servir de addCoffe(), addCream(),
addSugar().

3.8 Explosion de mthodes


Lorsque lon divise les tches entre diverses mthodes, il faut galement
prendre garde ne pas gnrer un nombre trop important de mthodes
Il y a donc un compromis respecter entre la maximisation de la cohsion
des mthodes et le fait de limiter les mthodes un nombre raisonnable
Il faut en tous les cas garder prsent lesprit que lorsque vous programmerez en Java, vous ne dialoguerez pas (seulement) avec une machine virtuelle,
vous communiquerez votre code dautres programmeurs.

3.9 Directives de maximisation de cohsion


Le mantra de la maximisation de cohsion Toujours essayer de maximiser la cohsion des mthodes. Focaliser chaque mthode sur une tche
conceptuelle et lui donner un nom qui indique clairement la nature de cette
tche.
La directive dobservation-de-ce-qui-est-transmis viter de passer
des donnes de contrle (pour dcider comment raliser le traitement) aux
mthodes
Le principe daversion de lexplosion de mthode quilibrer la maximisation de cohsion de mthode (qui augmente le nombre de mthodes
dans une classe) avec le maintien du nombre de mthodes une valeur
raisonnable
La rgle dor Lors de la conception de logiciel, ne pas faire aux autres
programmeurs (qui vont maintenir le logiciel) ce que lon naimerait se voir
faire (si lon devait maintenir leur logiciel).

III Programmation oriente objet en Java


Rfrences bibliographiques
Understanding Object Oriented Programming with Java, T. Budd [Bud98],
Design Patterns, E. Gamma, R. Helm, R. Johnson et J. Vlissides [GHJV95].

0.10 Notion de paradigmes


Paradigme : pour un tudiant du moyen-ge, phrase exemple. Peut tre
utilise comme modle ou comme aide dans lapprentissage dune langue.
Langages vus comme une forme de littrature . . .
Classe :
En-tte ( public class nomClasse { ...})
Corps :
champs de donnes ou
mthodes

III.1

Exemple 1 : balles

1.1 BallWorld
import java.awt.*;
import java.awt.event.*;
public class BallWorld extends Frame {
public static void main (String [ ] args)
{
BallWorld world = new BallWorld (Color.red);
world.show ();
}

49

50

Support de cours Java


private
private
private
private

static final int FrameWidth = 600;


static final int FrameHeight = 400;
Ball aBall;
int counter = 0;

private BallWorld (Color ballColor) {


// constructeur pour le nouveau monde
// on change la taille du cadre
setSize (FrameWidth, FrameHeight);
setTitle ("Ball World");
// initialiser les champs de donnees
aBall = new Ball (10, 15, 5);
aBall.setColor (ballColor);
aBall.setMotion (3.0, 6.0);

//
//
//
//

7
8
9
10

//
//
//
//
//

11
12
13
14
15

//
//
//
//

16
17
18
19

}
public void paint (Graphics g) {
// dabord, afficher la balle
aBall.paint (g);
// puis la bouger legeremenet
aBall.move();
// et traiter les cas de debordement
if ((aBall.x() < 0) || (aBall.x() > FrameWidth))
aBall.setMotion (-aBall.xMotion(), aBall.yMotion());
if ((aBall.y() < 0) || (aBall.y() > FrameHeight))
aBall.setMotion (aBall.xMotion(), -aBall.yMotion());
// enfin, reafficher le cadre
counter = counter + 1;
if (counter < 2000) repaint();
else System.exit(0);
}
}

Dclarations en lignes 710 : constantes ( public static final) ne peuvent


tre modifies
Utilits dun constructeur ; vite les 2 erreurs suivantes :
Un objet est cr, mais utilis avant dtre initialis. Ex. en C
struct enreg {
int no;
char nom[255];
};
main(int argc, char *argv[]) {

III.1 Exemple 1 : balles

51

struct enreg moi, *ptrtoi;


printf("Moi: no+10 : %d
nom : %s\n", moi.no+10, moi,nom);
printf("Toi: no : %d
nom : %s\n", ptrtoi->no, ptrtoi->moi,nom);
}

Un objet est cr et initialis plusieurs fois avant dtre utilis.


Constructeur en lignes 1119
La classe BallWorld est une sous classe de Frame : un cadre (fentre)
graphique
AWT (Abstract Window Toolkit) show() et paint()

1.2 Classe Frame


Classe Frame : fentre principale dapplication, pouvant changer de taille
Peut avoir un titre, une barre de menu, une icne, un curseur
Mthodes :

methode()

setCursor(int cursorType)
setIconImage(Image image)
setMenuBar(MenuBar mb)
setResizable(boolean resizable)
setTitle(String title)
dispose()

But
choisit le type de curseur
choisit licne
choisit le menu
peut changer de taille ou pas
choisit le titre
redfinition de Window.dispose()

Mthodes analogues setXXX() : getXXX()


Sous classe de Window elle-mme sous classe de Component

1.3 Modle graphique en Java


Java AWT (Abstract Window Toolkit) : exemple de cadre logiciel
Cadre logiciel : fournit la structure dun programme, mais pas les dtails
de lapplication
Le contrle gnral, le flux dexcution, est fourni par le cadre, qui na pas
besoin dtre rcrit
Exemple : la mthode show() ; la fentre est cre et limage de la fentre
doit tre dessine
Pour cela show() appelle la mthode paint(), passant en argument un
objet graphique

52

Support de cours Java


Le programmeur dfinit lapparence de la fentre en implantant la mthode paint()
Lobjet graphique en argument permet de dessiner des lignes, des polygnes,
. . . et dafficher du texte
Cas de la classe BallWorld
public void paint(Graphics g) {
// Dabord dessiner la balle
aBall.paint(g);
// ensuite la bouger legerement
aBall.show();
if ( (aBall.x() < 0) || (aBall.x() > FrameWidth) )
aBall.setMotion(-aBall.xMotion(), aBall.yMotion());
if ( (aBall.y() < 0) || (aBall.y() > FrameWidth) )
aBall.setMotion(-aBall.xMotion(), -aBall.yMotion());
// finalement, redessiner le cadre (frame)
counter = counter + 1;
if (counter < 2000)
repaint();
else
System.exit();

La mthode paint() produit limage de la balle


Elle permet galement de ractualiser la position de la balle (on la bouge
un peu ; si elle sort du cadre, on inverse la direction)
La mthode repaint() (aussi hrite de Frame) indique AWT que la
fentre doit tre redessine
Le programmeur appelle repaint() qui, elle, appellera ventuellement paint().
Il nappelle pas directement paint()

1.4 La classe Ball


//
//

abstraction de balle rebondissante generale et reutilisable

import java.awt.*;
public class Ball
protected
protected
protected
protected

{
Rectangle location;
double dx;
double dy;
Color color;

public Ball (int x, int y, int r)

III.1 Exemple 1 : balles

53

{
location = new Rectangle(x-r, y-r, 2*r, 2*r);
dx = 0;
dy = 0;
color = Color.blue;
}
// fonctions fixant les attributs
//
public void setColor (Color newColor)
{ color = newColor;

public void setMotion (double ndx, double ndy)


{ dx = ndx; dy = ndy; }
// fonctions daccess aux attributs de la balle
//
(Methodes de consultation de letat de lobjet)
//
public int radius()
{ return location.width / 2; }
public int x()

return location.x + radius(); }

public int y()

{ return location.y + radius(); }

public double xMotion()

{ return dx; }

public double yMotion()

{ return dy; }

public Rectangle region()

{ return location; }

// fonctions de modification dattributs de la balle


//
(Methodes de changement detat de lobjet)
//
public void moveTo(int x, int y)
{ location.setLocation(x, y); }
public void move()

{ location.translate ((int) dx, (int) dy); }

public void paint (Graphics g)


{
g.setColor (color);
g.fillOval (location.x, location.y,
location.width, location.height);

54

Support de cours Java


}

Les 4 champs de donnes sont dclars protected :


Le champ location, un Rectangle dans lequel est dessine la balle ,
Les champs dx, dy, les directions horizontale et verticale de la balle,
Le champ color, la couleur de la balle.
les fils ventuels peuvent y accder, pas les autres.
BONNE PRATIQUE : dclarer les donnes protected, plutt que private
6 mthodes permettent daccder aux attributs de la balle : radius(), x(),
y(), xMotion(), yMotion(), region(). Ce sont des fonctions daccs.
PRATIQUE FORTEMENT ENCOURAGE, plutt que davoir des champs
de donnes publics.
Permet de contrler strictement la modification des donnes
Certaines de ces fonctions utilisent des oprations fournies par la classe
rectangle

La mthode paint() utilise 2 fonctions de la classe Graphics : setColor()


et fillOval()

1.5 Multiples objets de la mme classe


On a ici un tableau de balles ballArray instanci dans le constructeur de la
classe MultiBallWorld().
import java.awt.*;
import java.awt.event.*;
public class MultiBallWorld extends Frame {
public static void main (String [ ] args)
{
MultiBallWorld world = new MultiBallWorld (Color.red);
world.show ();
}
private
private
private
private
private

static final int


static final int
Ball []
static final int
int

FrameWidth
FrameHeight
ballArray;
BallArraySize
counter

= 600;
= 400;
= 6;
= 0;

private MultiBallWorld (Color ballColor) {


// on redimensionne le cadre

III.1 Exemple 1 : balles

55

setSize (FrameWidth, FrameHeight);


setTitle ("Ball World");
// initialisation des champs de donnees
ballArray = new Ball [ BallArraySize ];
for (int i = 0; i < BallArraySize; i++) {
ballArray[i] = new Ball(10, 15, 5);
ballArray[i].setColor (ballColor);
ballArray[i].setMotion (3.0+i, 6.0-i);
}
}
public void paint (Graphics g) {
for (int i = 0; i < BallArraySize; i++) {
ballArray[i].paint (g);
// la deplacer legerement
ballArray[i].move();
if ((ballArray[i].x() < 0) || (ballArray[i].x() > FrameWidth))
ballArray[i].setMotion(-ballArray[i].xMotion(),
ballArray[i].yMotion());
if ((ballArray[i].y() < 0) || (ballArray[i].y() > FrameHeight))
ballArray[i].setMotion(ballArray[i].xMotion(),
-ballArray[i].yMotion());
}
// enfin, redessiner le cadre
counter = counter + 1;
if (counter < 2000) repaint();
else System.exit(0);
}
}

Un tableau de balles est cr en 3 temps :


1) private Ball[] ballArray ; // declaration de ballArray
2) ballArray = new Ball[BallArraySize] ;
//allocation memoire pour
les references BallArray[i]

3) for(int i = 0; i < BallArraySize; i++) {


ballArray[i] = new Ball(10, 15, 5);
} // allocation memoire pour chacune des instances de Ball

Chacune des balles sanime indpendamment des autres

56

Support de cours Java

III.2

Exemple 2 : Flipper

2.1 La classe PinBallGame, 1iere version


import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
public class PinBallGame extends Frame {
public static void main (String [ ] args) {
world = new PinBallGame();
world.show();
}
public static final int FrameWidth = 400;
public static final int FrameHeight = 400;
public static PinBallGame world;
private Vector balls;
public PinBallGame () {
setTitle ("Pin Ball Construction Kit");
setSize (FrameWidth, FrameHeight);
balls = new Vector();
addMouseListener (new MouseKeeper());
}
private class PinBallThread extends Thread { ... }
private class MouseKeeper extends MouseAdapter { ... }
public void paint (Graphics g) {
g.setColor (Color.white);
g.fillRect (FrameWidth-40, FrameHeight-40, 30, 30);
g.setColor (Color.red);
g.fillOval (FrameWidth-40, FrameHeight-40, 30, 30);
// dessiner les boules
for (int i = 0; i < balls.size(); i++) {
Ball aBall = (Ball) balls.elementAt(i);
aBall.paint(g);
}
}

III.2 Exemple 2 : Flipper

57

Pulieurs balles, nombre inconnu lavance utilisation de Vector : tableau croissance dynamique
Pas besoin de spcifier le type des lments dun vecteur
Un vecteur contient des Object les types primitifs ( int, . . . ) doivent
tre convertis en leurs objets associs ( Integer, . . . ) Accs un lment
doit tre suivi dun cast : Ball aBall = (Ball) balls.elementAt(i) ;

2.2 couteurs souris


Modle vnements de Java centr autour des couteurs, objets qui coutent
les vnements qui arrivent
Les couteurs sont spcifis via une interface. Pour les vnements de souris :
public interface MouseListener {
public void mouseClicked(MouseEvent e);
...
public void mouseReleased(MouseEvent e);
....
}

Interface java : description de comportement. Pas dimplantation de mthode. Seuls champs de donnes permis dclars static
Hritage : un enfant hrite de la structure de ses parents et de la possibilit
dimiter le comportement des parents.
Interface : pas de corps de mthode dans une interface parent Une classe
enfant hrite seulement de la spcification des mthodes et non de
la structure (champs de donnes, corps de mthodes)
Une interface peut tre utilise comme type de donnes. La variable
doit tre une instance de classe implantant linterface
Une classe implantant une interface doit fournir le corps de toutes les mthodes adaptateurs : classes implantant linterface avec toutes les mthodes corps vide : {}
Exemple dadaptateur pour les couteurs souris (interface MouseListener) :
classe MouseAdapter
Lutilisateur dfinit une classe hritant de MouseAdapter avec une redfinition des seules mthodes ncessaires.
Ici, seule mousePressed() est redfinie, dans la classe MouseKeeper.
private class MouseKeeper extends MouseAdapter {
public void mousePressed (MouseEvent e) {

58

Support de cours Java


int x = e.getX();
int y = e.getY();
if ((x > FrameWidth-40) && (y > FrameHeight -40)) {
PinBall newBall = new PinBall(e.getX(), e.getY());
balls.addElement (newBall);
Thread newThread = new PinBallThread (newBall);
newThread.start();
}
}

Une instance est cre, puis passe : addMouseListener(new MouseKeeper()) ;


Largument des mthodes dun MouseListener est un MouseEvent : il
contient des informations sur lvnement survenu. Ici on sintresse aux
coordonnes de la souris

2.3 Plusieurs activits (threads)


Une activit ( thread) par balle pour son mouvement
public class PinBallGame extends Frame {
...
private class PinBallThread extends Thread {
private Ball theBall;
public PinBallThread (Ball aBall) {
theBall = aBall;
}
public void run () {
while (theBall.y() < FrameHeight) {
theBall.move ();
// Dautres actions viendront ici
repaint();
// on prend un peu de repos
try {
sleep(10);
} catch (InterruptedException e) { System.exit(0); }
}
}
}
...
}

III.2 Exemple 2 : Flipper

59

Notez : on doit toujours appeler start() et implanter run(). On ne doit


pas appeler directement run()
Traitement de lexception dinterruption (touche ^C)

2.4 Ajout de cibles : hritage et interfaces


On veut traiter toutes les cibles de manire uniforme pour certaines
oprations.
Chaque cible tant toutefois reprsente de manire interne par une structure de donnes diffrente
Pas dutilisation de lhritage pour les cibles. Plus prcisment :
PinBall (boule de flipper) est un type particulier de Ball (boule)
avec des caractristiques en plus (peut sexcuter comme une tche
spare) hritage.
Peu de choses en commun entre un Peg (piquet : cible qui, lorsquelle est
touche, compte un nombre de points et renvoie la balle dans une autre
direction) et un Wall (mur : qui rflchit le mouvement de la balle)
un partage de certains comportements via une interface
Linterface PinBallTarget scrit
interface PinBallTarget {
public boolean intersects (Ball aBall);
public void moveTo (int x, int y);
public void paint (Graphics g);
public void hitBy (Ball aBall);
}

Plus prcisment, chaque cible


peut savoir si une boule entre en contact avec elle, via intersects() :
sil y a une intersection entre elle et la rgion couverte par la boule ;
peut tre dplace en un point de la surface de jeu via moveTo() ;
peut se dessiner via paint() ;
peut ragir lorsquelle est touche par une boule via hitBy().
Chaque cible aura une implantation diffrente de ces oprations

2.5 Cible Spring


Cas de Spring (ressort) :
class Spring implements PinBallTarget {
private Rectangle pad;
private int state;
public Spring (int x, int y) {

60

Support de cours Java


pad = new Rectangle(x, y, 30, 3);
state = 1;
}
public void moveTo (int x, int y)
{ pad.setLocation(x, y); }
public void paint (Graphics g) {
int x = pad.x;
int y = pad.y;
g.setColor(Color.black);
if (state == 1) {
g.fillRect(x, y, pad.width, pad.height);
g.drawLine(x, y+3, x+30, y+5);
g.drawLine(x+30, y+5, x, y+7);
g.drawLine(x, y+7, x+30, y+9);
g.drawLine(x+30, y+9, x, y+11);
}
else {
g.fillRect(x, y-8, pad.width, pad.height);
g.drawLine(x, y+5, x+30, y-1);
g.drawLine(x+30, y-1, x, y+3);
g.drawLine(x, y+3, x+30, y+7);
g.drawLine(x+30, y+7, x, y+11);
state = 1;
}
}
public boolean intersects (Ball aBall)
{ return pad.intersects(aBall.location); }
public void hitBy (Ball aBall) {
// on sassure que lon monte
if (aBall.dy > 0)
aBall.dy = - aBall.dy;
// on booste un peu la boule
aBall.dy = aBall.dy - 0.5;
state = 2;
}
}

Lorsquune boule qui descend entre en contact avec lui, le Spring fait
rebondir la balle.
Il est reprsent par un rectangle (aplati) et une srie de segments en

III.2 Exemple 2 : Flipper

61

zigzag
Il y a intersection ressort-boule si le rectangle aplati du ressort et celui
circonscrit la boule sintersectent
2 reprsentations graphiques du ressort, contrl par state :
tat comprim ( state == 1) : tat de dpart
tat dtendu ( state == 2) : dclench par le choc dune boule (dans
hitBy())

2.6 Cible Wall


Cas de Wall (mur) :
class Wall implements PinBallTarget {
public Rectangle location;
public Wall (int x, int y, int width, int height)
{ location = new Rectangle(x, y, width, height); }
public void moveTo (int x, int y)
{ location.setLocation (x, y); }
public void paint (Graphics g) {
g.setColor(Color.black);
g.fillRect(location.x, location.y,
location.width, location.height);
}
public boolean intersects (Ball aBall)
{ return location.intersects(aBall.location); }
public void hitBy (Ball aBall) {
if ( (aBall.y() < location.y) ||
(aBall.y() > (location.y + location.height)) )
aBall.dy = - aBall.dy;
else
aBall.dx = - aBall.dx;
}
}

Reprsent par un rectangle (aplati).


Une balle entre en contact avec un mur si leurs rectangles sintersectent.
La balle rebondit alors sur le mur dans la direction oppose de lincidente.
Une variable de type PinBallTarget peut tre aussi bien une rfrence
une instance de Spring que de Wall : un des aspects du polymorphisme

62

Support de cours Java

2.7 Cible Hole


3ieme type de cible : Hole (trou) agit comme un puits boules :
il est reprsent par un cercle color, comme une Ball (boule)
il a une position sur laire de jeu, comme une Ball (quoique ayant une
position fixe)
un Hole est structurellement similaire une Ball. On utilise donc
lhritage
De plus Hole est une cible et implante ce titre linterface PinBallTarget.
Donc, Hole :
hrite en particulier du comportement de Ball, et donc des mthodes
paint() et moveTo() ;
doit implanter toutes les mthodes de PinBallTarget implantation de
intersects() et hitBy() ;
voit la mthode setMotion() redfinie pour ne rien faire. Hole est une
Ball statique

2.8 Cible ScorePad


La cible ScorePad (zone bonus) est une rgion circulaire fixe, comme
un trou. Lorsquune boule passe dessus, le carr bonus ajoute un certain
montant au score du joueur.
ScorePad hrite de Hole. Une classe hritant dun parent qui implante
une interface doit implanter elle-mme cette interface. ScorePad redfinit
paint() et hitBy()
On ajoute un label (zone de texte en lecture seulement) linterface : score
du joueur
Notez : la mthode addScore() est dclare synchronized. Deux boules
peuvent trs bien passer sur le mme ScorePad en mme temps un score
peut tre indfini. Si addScore() est synchronized, 2 threads (activits) ne
peuvent lexcuter en mme temps

2.9 Cible Peg


Un Peg (piquet ou champignon) est similaire un ScorePad :
aspect circulaire, de petite tour ;
augmentation de score quand une boule entre en contact avec ;
la boule est rflchie quand elle butte dessus ;
loi de rflexion simpliste (normalement loi de Descartes) et attente que la
boule nait plus dintersection avec la cible ;

III.3 Formes dhritage

63

2 reprsentations graphiques : tat de base, comprim et tat venant dtre


touch, dtendu.

2.10 Autres versions du jeu palettes


2e version du jeu : avec un vecteur de cibles
3e version du jeu : le joueur peut composer lui-mme laire de jeu en plaant
les diverses cibles
Les cibles sont reprsentes dans une palette tout au long de la bordure
gauche. On clique sur la cible dsire et on la fait glisser o lon veut (click
and drag of the desired target)
Ralis par redfinition de mousePressed() et mouseReleased() :
mousePressed() et mouseReleased() communiquent via la variable element.
mousePressed() cr une cible potentielle.
mouseReleased() vrifie si les coordonnes de la souris au relchement
sont lintrieur de laire de jeu ; si oui, ET si une cible tait slectionne
( element != null), la cible est ajoute au vecteur de cibles.

III.3

Formes dhritage

3.1 Hritages
En Java, lhritage stricto sensu (mot cl extends) est la sous classification.
Toutefois, beaucoup de points communs entre lhritage de spcification (mot cl implements) et la sous classification, ou hritage de code.
Hritage :
le comportement et les donnes des classes enfants sont une extension des proprits des classes parents (mot cl extends).
Mais galement :
une classe enfant est une version plus spcialise (ou restreinte)
dune classe parent ; cest donc galement une contraction dune
classe parent.
Exemple : un Frame est tout type de fentre, mais un PinBallGame est un
type bien de fentre particulier.
Les Extension/Contraction donnent plusieurs usages de lhritage.

3.2 Classe Object


Classe Object : dont toutes les classes Java drivent.
Fonctionnalits minimales dans Object incluent :

64

Support de cours Java

methode()

But

equals(Object obj)

teste lgalit de lobjet argument et de


lappelant. Souvent redfinie.
Renvoie une chane contenant le nom de la
classe.
Renvoie la valeur de hachage pour cet objet. Doit tre dfinie lorsque equals()
lest.
Renvoie une chane associe lobjet pour
affichage. Souvent redfinie.

getClass()
hashCode()

toString()

3.3 Substituabilit
Substituabilit : le type donn lors de la dclaration dune variable peut
diffrer de celui de linstance rfrence par la variable.
Exemple : dans le jeu de flipper, la variable target est dclare comme
PinBallTarget, mais correspond beuacoup de cibles diffrentes (du vecteur
targets) PinBallTarget target = (PinBallTarget) target.elementAt(j) ;
Une variable dclare de type Object peut contenir (rfrencer une instance
de) tout type non primitif. Un vecteur contient des lments de type Object ;
on peut donc tout y stocker.
Une justification possible de la substituabilit par hritage est le
principe de substitution
(i) les instances de la sous classe doivent possder tous les champs
de donnes associs la classe parent ;
(ii) les instances de la sous classe doivent implanter, au moins par hritage, toutes les fonctionnalits dfinies pour la classe parent ;
(iii) donc une instance dune classe enfant peut imiter le comportement
dune classe parent et tre indistinguable dune instance de la
classe parent lorsque substitue dans une situation similaire.
Ceci ne couvre pas toutes les utilisations de lhritage.

3.4 Sous type Sous classe


Sous type : relation entre 2 types reconnaissant explicitement le principe
de substitution.
Cest--dire, un type B est un sous type de A si :

III.3 Formes dhritage

65

une instance de B peut tre lgalement assigne une variable dclare


de type A ;
cette valeur peut alors tre utilise par la variable sans changement visible
de comportement.
Sous classe : mcanisme de construction dune nouvelle classe utilisant
lhritage. Se reconnat facilement la prsence du mot cl extends.
La relation de sous type est plus abstraite et se trouve peu documente
directement dans le source.
Dans la plupart des cas (mais pas toujours), une sous classe est galament
un sous type.
Des sous types peuvent tre forms en utilisant des interfaces, reliant
des types nayant pas de lien dhritage.

3.5 Hritage par spcialisation


Nouvelle classe : varit spcialise de la classe parent, mais satisfaisant les
spcifications du parent pour tous les aspects importants.
Cette forme cre toujours un sous type et le principe de substitution
est parfaitement respect.
Forme dhritage idale, rechercher.
Exemple du jeu de flipper : public class PinBallGame extends Frame ...
pour excuter lapplication, des mthodes comme setSize(), setTitle()
et show(), hrites de Frame, sont appeles.
Ces mthodes ne ralisent pas quelles manipulent une instance de la
classe PinBallGame.
Elles agissent comme si elles opraient sur une instance de Frame.
Lorsque ncessaire, une mthode redfinie pour lapplication (par ex.
paint()) est appele.

3.6 Hritage par spcification


But : garantir que les classes aient une interface commune, cest--dire
implantent les mmes mthodes.
La classe parent peut tre une combinaison doprations implantes
et doprations dlgues aux classes enfants.
Forme dhritage en fait cas particulier de lhritage par spcialisation, sauf
que les sous classes ne sont pas des raffinements dun type existant mais des
ralisations de spcifications abstraites.
Classe parent parfois nomme classe de spcification abstraite.

66

Support de cours Java


Forme dhritage idale, rechercher, respectant le principe de substitution.
Deux mcanismes, en Java, pour lhritage par spcification :
les interfaces (par ex. des cibles du jeu de flipper)
les classes abstraites
Exemple de classe abstraite : Number de la bibliothque Java (classe parent
de Integer, Long, Double, . . . ). Description de classe :
public abstract class Number {
public abstract int intValue();
public abstract int longValue();
public abstract int floatValue();
public abstract int doubleValue();
public abstract int byteValue() {
return (byte) intValue();
}
public abstract int shortValue() {
return (short) intValue();
}

Les mthodes dclares abstract doivent tre redfinies dans une sousclasse instanciable.
Hritage par spcification : la classe parent nimplante pas le comportement
mais dfinit celui qui doit tre implant dans les classes enfant.

3.7 Hritage par construction


Une classe peut hriter des fonctionnalits dsires dun parent, juste
en changeant le nom des mthodes, ou en modifiant des arguemnts. Peut
tre vrai mme sil ny a pas de relation conceptuelle entre les classes
parent et enfant.
Par exemple, dans le jeu de flipper, Hole est dclar comme sous classe de
Ball. Pas de relation logique entre les 2 concepts.
Mais dun Point de vue pratique, beaucoup du comportement de Hole
correspond celui de Ball. Gain de temps de dveloppement en utilisant
lhritage.
class Hole extends Ball implements PinBallTarget {
public Hole (int x, int y) {
super (x, y, 12);
setColor (Color.black);
}

III.3 Formes dhritage

67

public boolean intersects (Ball aBall)


{ return location.intersects(aBall.location); }
public void hitBy (Ball aBall) {
// mettre la boule hors champ
aBall.moveTo (0, PinBallGame.FrameHeight + 30);
// arreter le mouvement
aBall.setMotion(0, 0);
}
}

Autre exemple, de la bibliothque Java : Stack, construite par hritage


partir de Vector.
class Stack extends Vector {
public Object push(Object item) {
addElement(item);
return(item);
}
public boolean empty() {
return isEmpty();
}
public synchronized Object pop() {
Object obj = peek();
removeElementAt(size() - 1);
return obj;
}
public synchronized Object peek() {
return elementAt(size() - 1);
}

Forme dhritage parfois boude, parce que violant directement le principe de substitution.
Forme toutefois pratique et assez largement utilise.

3.8 Hritage par extension


Une classe enfant ajoute de nouveaux comportements et naltre pas
ceux hrits.
Exemple de la bibliothque Java : Properties hritant de Hashtable.

68

Support de cours Java


HashTable : structure de dictionnaire avec un ensemble de paires cls/valeurs.
Properties : utilis notamment pour les informations relatives lenvironnement dexcution. Par ex. le nom de lutilisateur, al version de
linterprteur, le nom du systme dexploitation.
Properties utilise les mthodes de HashTable pour stocker et retirer des
paires nom/valeur.
Elle dfinit galement dautres mthodes :
load(), pour lire du disque,
save(), pour stocker sur disque,
getProperty(),
propertyNames(),
list().
Les fonctionnalits du parent restant intactes, cette forme dhritage respecte le principe de substitution et les sous classes sont des sous types.

3.9 Hritage par limitation


Le comportement de la sous-classe est plus restreint que celui du parent.
Forme caractrise par la prsence de mthodes qui rendent une opration permise pour le parent illgale.
Cette forme dhritage contredit explicitement le principe de substitution. lviter autant que possible.

3.10 Hritage par combinaison


Une classe hritant de 2 parents ou plus, c..d. de lhritage multiple.
Une sous classe Java ne peut tre forme par hritage partir de plus dune
classe parent. Mais plusieurs formes trs analogues existent.
Dabord, il est commun pour une nouvelle classe dtendre une classe
existante et dimplanter une interface. Par ex., dans le jeu de flipper :
class Hole extends Ball implements PinBallTarget

...

Ensuite, une classe peut implanter plus dune interface. Par exemple,
dans la bibliothque dE/S Java, un RandomAcessFile implante la fois les
protocoles DataInput et DataOutput.

3.11 Rsum des formes dhritage


Dans ce qui suit, le sigle (PS) dsigne une forme dhritage qui respecte le
principe de sunstitution.

III.3 Formes dhritage

69

Spcialisation(PS) : la classe enfant est un cas particulier de la classe parent,


c..d. un sous type.
Spcification(PS) : la classe parent dfinit un comportement implant dans
la classe enfant et non dans la classe parent.
Construction : la classe enfant utilise le comportement fourni par le
parent mais nen est pas un sous type.
Extension(PS) : la classe enfant ajoute de nouvelles fonctionnalits la
classe parent, mais ne modifie aucun comportement hrit.
Limitation : la classe enfant restreint lutilisation de certains comportements hrits de la classe parent.
Combinaison(PS) : la classe enfant hrite de caractristiques de plus
dune classe parent. Bien que lhritage multiple ne soit pas intgr
Java, des formes analogues fournissent les mmes bnfices ; les classes
peuvent utiliser lhritage et limplantation dinterface et une classe peut
implanter plusieurs interfaces.
: le langage Java suppose implicitement que les sous classes sont galement des sous types. une instance de sous classe peut tre assigne
une variable dclare du type parent. Cependant, cette supposition que
les sous classes sont des sous types nest pas toujours valide, plus
prcisment dans les cas dhritage par construction et par limitation. Ceci
peut tre une source directe derreurs.

3.12 Modificateurs
Plusieurs modificateurs peuvent altrer divers aspects du processus dhritage.
Modificateurs de visibilit (ou de contrle daccs) : public, protected,
private

Modificateurs daccessibilit :
static : on ne peut redfinir une mthode static
abstract : classes non instanciables ; ne peut tre utilis que comme type
parent.
final : linverse du prcdent. On ne peut crer de sous classe dune
classe final.

3.13 Bnfices de lhritage


Un programmeur peut soit agir comme dveloppeur, soit comme utilisateur
de composants rutilisables.

70

Support de cours Java


La construction de logiciels devient alors une entreprise grande chelle,
o chacun cr certains composants et en utilise dautres, disponibles sur le
march.
Les mcanismes de production et dutilisation de ces composants ont t
codifis : les composants ainsi crs sont les beans Java (ou grains, c..d. de
la matire premire pour la production de caf, dja slectionne et traite).
Rutilisabilit logicielle Un code hrit na pas besoin dtre rcrit.
Robustesse renforce Du code frquemment utilis contient moins
derreurs que du code peu utilis. Les mmes composants rutiliss dans
plusieurs applications verront leurs erreurs plsu rapidement dcouvertes que
ceux utiliss une fois. Le cot de maintenance logicielle peut galement tre
partag entre plusieurs projets.
Partage de code Intervient plusieurs niveaux :
Plusieurs utilisateurs ou projets partagent les mmes classes.
Deux ou plusieurs classes dveloppes par un membre dun projet hritent
dune classe parente.
Cohrence de linterface Lorsque 2 classes ou plus hritent de la mme
super classe, on est sr que le comportement hrit sera identique dans tous
les cas.
Composants logiciels Lhritage permet la onstruction de composants
rutilisables. ceci rduit dautant le codage.
Prototypage rapide En utilisant des composants rutilisables, une premire version peut tre rapidement labore puis raffine par itrations successives avec les utilisateurs du produit.
Polymorphisme et cadres logiciels Les logiciels sont souvent crits de
manire ascendante (ou bottom up) et sont conus de manire descendante
(top down). La portabilit dcrot lorsquon gravit les niveaux dabstraction.
Le polymorphisme permet de gnrer des composants rutilisables
un haut niveau dabstraction ; il suffit pour les rutiliser de changer quelques composants de plus bas niveau. La bibliothque AWT est un
exemple de cadre logiciel qui sappuie sur lhritage et la substitution.
Protection de linformation Lutilisateur dun composant na besoin
de comprendre que la nature dun composant et son interface.
le couplage entre systmes logiciels est rduit. Le couplage est lune des
principales causes de complexit des logiciels.

3.14 Contreparties : cot de lhritage


Vitesse dexcution La vitesse dexcution dun assemblage de composants rutilisables est souvent moindre que celle de code spcialis. Mais :
La diffrence en vitesse dexcution est souvent peu leve.

III.4 Exemple 3 : jeu de solitaire

71

Elle est compense par une plus grande vitesse de dveloppement


logiciel et un cot de maintenance moindre.
Il ne faut surtout pas tre obnubil par la vitesse dexcution, mais dvelopper dabord sainement. Puis dterminer les goulots dtranglement
et optimiser les seules parties qui en ont besoin. Rappel de la remarque
de Bill Wulf1 Plus de pchs informatiques sont commis au nom de lefficacit (sans ncessairement latteindre) que pour toute autre raison y
compris le crtinisme le plus viscral.
Taille des programmes Lutilisation des bibliothques tend faire crotre
la taille du code final. Point peu important avec la chute de prix des mmoires.
Surcharge du passage de message En gnral peu important. Contrebalanc par les bnfices de lhritage.
Complexit des programmes Il se peut quun graphe dhritage complexe ncessite plusieurs aller-retours du haut en bas de ce graphe pour bien
le comprendre. Cest le problme du yoyo

III.4

Exemple 3 : jeu de solitaire

4.1 Classe Card


import java.awt.*;
public class Card {
// Constantes publiques pour les cartes et couleurs
final public static int width
= 50;
final public static int height = 70;
final public static int heart
= 0;
final public static int spade
= 1;
final public static int diamond = 2;
final public static int club
= 3;
// Champs de donnees privees pour le rang et la couleur
private boolean
faceup;
private int
r;
private int
s;
Card (int sv, int rv)
{ s = sv; r = rv; faceup = false; }
// access attributes of card
1

W.A. Wulf A case Against the GOTO, Proc. of the 25th National ACM Conf., 1972.

72

Support de cours Java


// Acces aux attributs de la carte
//
(methodes de visualisation detat)
//
public int rank () { return r; }
public int suit() { return s; }
public boolean faceUp() { return faceup; }
public void flip() { faceup = ! faceup; }
public Color color() {
if (faceUp())
if (suit() == heart || suit() == diamond)
return Color.red;
else
return Color.black;
return Color.yellow;
}
public void draw (Graphics g, int x, int y) { ... }

Pour chaque carte :


une valeur de couleur (pique, coeur, carreau, trfle),
une valeur de rang (2-10, V, D, R, A).
Variables dinstance dclares private et accessibles via des fonctions
daccs.
Ce type daccs assure entre autres que la rang et la couleur ne peuvent
tre modifis arbitrairement.
Les hauteur, largeur et couleurs sont dclares comme static final (c..d.
des constantes).
La plupart des mthodes sont dclares final :
sert comme documentation au lectuer indiquant que ces mthodes ne
peuvent tre redfinies ;
elles peuvent ventuellement tre optimises.
Une carte peut :
initialiser son tat (via le constructeur),
renvoyer son tat ( rank(), suit(), faceUp(), color()),
se retourner ( flip()),
se dessiner ( draw()).
public class Card {

III.4 Exemple 3 : jeu de solitaire

73

...
public void draw (Graphics g, int x, int y) {
String names[] = {"A", "2", "3", "4", "5", "6",
"7", "8", "9", "10", "J", "Q", "K"};
// effacer le rectangle et dessiner le contour
g.clearRect(x, y, width, height);
g.setColor(Color.blue);
g.drawRect(x, y, width, height);
// dessiner le corps de la carte
g.setColor(color());
if (faceUp()) {
g.drawString(names[rank()], x+3, y+15);
if (suit() == heart) {
g.drawLine(x+25, y+30, x+35, y+20);
g.drawLine(x+35, y+20, x+45, y+30);
g.drawLine(x+45, y+30, x+25, y+60);
g.drawLine(x+25, y+60, x+5, y+30);
g.drawLine(x+5, y+30, x+15, y+20);
g.drawLine(x+15, y+20, x+25, y+30);
}
else if (suit() == spade) {
g.drawLine(x+25, y+20, x+40, y+50);
g.drawLine(x+40, y+50, x+10, y+50);
g.drawLine(x+10, y+50, x+25, y+20);
g.drawLine(x+23, y+45, x+20, y+60);
g.drawLine(x+20, y+60, x+30, y+60);
g.drawLine(x+30, y+60, x+27, y+45);
}
else if (suit() == diamond) {
g.drawLine(x+25, y+20, x+40, y+40);
g.drawLine(x+40, y+40, x+25, y+60);
g.drawLine(x+25, y+60, x+10, y+40);
g.drawLine(x+10, y+40, x+25, y+20);
}
else if (suit() == club) {
g.drawOval(x+20, y+25, 10, 10);
g.drawOval(x+25, y+35, 10, 10);
g.drawOval(x+15, y+35, 10, 10);
g.drawLine(x+23, y+45, x+20, y+55);
g.drawLine(x+20, y+55, x+30, y+55);
g.drawLine(x+30, y+55, x+27, y+45);
}
}

74

Support de cours Java


else { // face cachee
g.drawLine(x+15, y+5,
g.drawLine(x+35, y+5,
g.drawLine(x+5, y+20,
g.drawLine(x+5, y+35,
g.drawLine(x+5, y+50,
}
}

x+15,
x+35,
x+45,
x+45,
x+45,

y+65);
y+65);
y+20);
y+35);
y+50);

Une caret connt sa valeur et sait se dessiner. De cette manire, linformation


est encapsule et isole de lapplication utilisant la carte jouer.
Pour porter lapplication sur une plateforme avec des routines graphiques
spcifiques, seule la mthode draw() doit tre rcrite.

4.2 Rgles du jeu


Version Klondie du solitaire2
On utilise un paquet de 52 cartes.
Le tableau (table de jeu) consiste en 28 cartes rparties en 7 piles. La
1 ire pile a 1 carte, la 2 ime en a 2, etc. jusqu 7.
La carte du haut de chaque pile est initialement face visible (c..d. couleur
visible). Les autres sont face cache.
Les piles de couleur (ou fondements) se construisent des as aux rois selon
la couleur. Elles sont remplies avec les cartes disponibles.
But du jeu : placer les 52 cartes dans les piles de couleur.
Les cartes ne faisant pas partie du tableau sont intialement dans la pioche.
Elles y sont face cache et sont prises une par une, puis places, face visible,
sur la pile de dfausse (ou de mise lcart).
De l, elles peuvent tre places sur une pile tableau ou sur une pile de
couleur.
On tire des cartes jusqu ce que la pioche soit vide. ce moment l, le jeu
est termin si aucun autre mouvement nest possible.
Les cartes sont places sur une pile tableau seulement sur une carte
dun rang au dessus et dune couleur oppose.
Les cartes sont places sur une pile de couleur seulement si elles sont de
mme couleur et dun rang au dessus ou si la pile est vide et que
la carte est un as.
Les places vides dans le tableau qui surviennent pendant le jeu ne peuvent
tre remplies que par des rois.
2

A.H. Morehead et G. Mott-Smith, The Complete Book of Solitaire and Patience Games,
Grosset & Dunlap, New York, 1949.

III.4 Exemple 3 : jeu de solitaire

75

La carte du dessus de chaque pile tableau et la carte du dessus de la pile


dfausse sont toujours disponibles.
La seule fois o plus dune carte peut tre dplace est lorsquun ensemble
de cartes faces visibles dun tableau, nomm une construction, est dplac
vers une autre pile tableau.
Ceci nest possible que si la carte la plus en dessous de la construction peut
tre joue lgalement sur la carte la plus au dessus de la destination.
Transfert de constructions non supprot ici.
La carte la plus au dessus dun tableau est toujours face visible.
Si une carte dun tableau dplace laisse sur le dessus de la pile une carte
face cache, cette dernire peut tre retourne.

4.3 Hritage des piles de cartes


Variables dinstance protected :
coordonnes en haut gauche de la pile,
une Stack, lensemble des cartes.
import java.awt.*;
import java.util.Stack;
import java.util.EmptyStackException;
public class CardPile {
// coordonnees de la pile de cartes
protected int x;
protected int y;
protected Stack thePile;
CardPile (int xl, int yl)
{ x = xl; y = yl; thePile = new Stack(); }
// Lacces aux cartes nest pas redefini
//
public final Card top() { return (Card) thePile.peek(); }
public final boolean isEmpty() { return thePile.empty(); }
public final Card pop() {
try {
return (Card) thePile.pop();
} catch (EmptyStackException e) { return null; }
}

76

Support de cours Java


// Les suivantes sont certaines fois redefinies
//
public boolean includes (int tx, int ty) {
return x <= tx && tx <= x + Card.width &&
y <= ty && ty <= y + Card.height;
}
public void select (int tx, int ty) {
// ne rien faire
}
public void addCard (Card aCard)
{ thePile.push(aCard); }
public void display (Graphics g) {
g.setColor(Color.blue);
if (isEmpty())
g.drawRect(x, y, Card.width, Card.height);
else
top().draw(g, x, y);
}
public boolean canTake (Card aCard) {
return false;
}
}

3 mthodes dclares final, communes toutes les piles de cartes :


top(), permet de voir quelle est la carte du dessus dune pile.
isEmpty(), teste si une pile est vide.
pop(), retire la carte du dessus dune pile.
Traitement de Stack vide comme une exception dans pop().
5 mthodes peuvent tre redfinies :
include() dtermine si le point de coordonnes fournies en arguments est
lintrieur de la frontire (bord) de la pile. Est redfinie pour les piles
tableau afin de tester toutes les cartes.
canTake() Dit si une pile peut prendre une carte donne. Seules les piles
tableau et couleur peuvent prendre des cartes ; aussi laction par dfaut
est-elle non ( false). Redfinie pour les 2 piles mentionnes.
addCard() Ajoute une carte la pile. Redfini dans la pile de dfausse pour
tre sr que la carte soit face visible.
display() Affiche la pile de cartes. La mthode par dfaut affiche la carte

III.4 Exemple 3 : jeu de solitaire

77

du dessus de la pile. Redfinie dans la classe Tableau pour afficher une


colonne de cartes.
select() Effectue une action en rponse un clic de souris. Est appele
lorsque lutilisateur clique en un point lintrieur de la pile. Laction
par dfaut est de ne rien faire. Redfinie pour pour les piles tableau, de
pioche et de dfausse afin de jouer les cartes du dessus, si possible.
Bnfice de lhritage : au lieu de 25 mthodes implanter, 13 le sont
effectivement :
CardPile
includes()
canTake()
addCard()
display()
select()

X
X
X
X
X

SuitPile

DeckPile

DiscardPile

TablePile

X
X

X
X
X

X
X

4.4 Les piles couleur


class SuitPile extends CardPile {
SuitPile (int x, int y) { super(x, y); }
public boolean canTake (Card aCard) {
if (isEmpty())
return aCard.rank() == 0;
Card topCard = top();
return (aCard.suit() == topCard.suit()) &&
(aCard.rank() == 1 + topCard.rank());
}
}

Dans le constructeur super() est appel. Le constructeur est redfini,


comme dans toutes les autres piles, sinon super() est automatiquement
appele sans arguments.
canTake() est redfinie. Une carte peut tre place sur le dessus de la pile
si soit :
cest un as (elle a un rang == 0),
la carte est de la mme couleur que la carte du dessus de la pile et dun
rang suprieur (un 3 de trfle peut tre jou sur un 2 de trfle).

78

Support de cours Java

4.5 La pioche
class DeckPile extends CardPile {
DeckPile (int x, int y) {
// Initialiser le parent
super(x, y);
// Puis creer la pioche
// que lon place dabord dans une pile locale
for (int i = 0; i < 4; i++)
for (int j = 0; j <= 12; j++)
addCard(new Card(i, j));
// Ensuite, on bat les cartes
Random generator = new Random();
for (int i = 0; i < 52; i++) {
int j = Math.abs(generator.nextInt() % 52);
// Echanger 2 valeurs de cartes
Object temp = thePile.elementAt(i);
thePile.setElementAt(thePile.elementAt(j), i);
thePile.setElementAt(temp, j);
}
}
public void select(int tx, int ty) {
if (isEmpty())
return;
Solitare.discardPile.addCard(pop());
}
}

Lorsque construite, la pioche cre le jeu complet des 52 cartes. Une fois cr,
le jeu est mlang. Un gnrateur de nombres pseudo-alatoires est dabord
cr, puis chaque carte est change avec une, prise au hasard.
On ralise un accs altoire dans une pile. Logiquement, on veut restreindre laccs au seul lment du dessus. En Java, une Stack est construite
par hritage partir dun Vector. Donc la mthode elementAt() peut tre
applique une Stack. Cest un exemple dhritage par construction.
select() ne fait rien si la pioche est vide. Sinon, la carte du dessus est
enleve et ajoute la pile de dfausse.
Une variable static par pile est utilise. Ici, on se sert de Solitaire.discardPile.

III.4 Exemple 3 : jeu de solitaire

79

4.6 La pile de dfausse


class DiscardPile extends CardPile {
DiscardPile (int x, int y) { super (x, y); }
public void addCard (Card aCard) {
if (! aCard.faceUp())
aCard.flip();
super.addCard(aCard);
}
public void select (int tx, int ty) {
if (isEmpty())
return;
Card topCard = pop();
for (int i = 0; i < 4; i++)
if (Solitare.suitPile[i].canTake(topCard)) {
Solitare.suitPile[i].addCard(topCard);
return;
}
for (int i = 0; i < 7; i++)
if (Solitare.tableau[i].canTake(topCard)) {
Solitare.tableau[i].addCard(topCard);
return;
}
// personne ne peut lutiliser, la remettre dans la liste
addCard(topCard);
}
}

Deux formes dhritage diffrentes sont utilises. Dans la 1 ire , la


mthode select() redfinit ou remplace le comportement par dfaut.
Lorsque lon clique sur la souris au dessus de la pile, select() regarde si la
carte du dessus peut tre joue sur une pile couleur ou sur une pile tableau.
Sinon la carte est garde dans la pile de dfausse.
La 2 ime forme dhritage est un raffinement du comportement parental.
Dans addCard(), le comportement du parent est excut (via super()) et
on sassur que la carte est toujours face visible.
Un autre type de raffinement apparat dans les constructeurs.

80

Support de cours Java

4.7 Les piles tableau


class TablePile extends CardPile {
TablePile (int x, int y, int c) {
// initialisation de la classe parent
super(x, y);
// initialisation de la pile de cartes
for (int i = 0; i < c; i++) {
addCard(Solitare.deckPile.pop());
}
// retourner la carte du dessus face visible
top().flip();
}
public boolean canTake (Card aCard) {
if (isEmpty())
return aCard.rank() == 12;
Card topCard = top();
return (aCard.color() != topCard.color()) &&
(aCard.rank() == topCard.rank() - 1);
}
public boolean includes (int tx, int ty) {
// ne pas tester la carte du dessous
return x <= tx && tx <= x + Card.width &&
y <= ty;
}
public void select (int tx, int ty) {
if (isEmpty())
return;
// si elle est face cachee, la retourner
Card topCard = top();
if (! topCard.faceUp()) {
topCard.flip();
return;
}
// sinon, voir si une pile de couleur peut la prendre
topCard = pop();
for (int i = 0; i < 4; i++)
if (Solitare.suitPile[i].canTake(topCard)) {

III.4 Exemple 3 : jeu de solitaire

81
Solitare.suitPile[i].addCard(topCard);
return;
}

// sinon, voir si une autre pile peut la prendre


for (int i = 0; i < 7; i++)
if (Solitare.tableau[i].canTake(topCard)) {
Solitare.tableau[i].addCard(topCard);
return;
}
// sinon, la remettre dans notre pile
addCard(topCard);
}
public void display (Graphics g) {
int localy = y;
for (Enumeration e = thePile.elements(); e.hasMoreElements(); ) {
Card aCard = (Card) e.nextElement();
aCard.draw (g, x, localy);
localy += 35;
}
}
}

Lorsquinitialise, la pile tableau prend un ceratin nombre de cartes de la


pioche et les insre dans sa pile. Le nombre de ces cartes est donn en
argument au constructeur.
Une carte peut tre ajoute la pile ( canTake()) si la pile est vide et la carte
est un roi ou si la carte est de couleur oppose (couleur au sens rouge/noir)
celle de la carte du dessus et dun rang infrieur.
Lors dun test de position dun clic de souris ( includes()), seuls les bords
gauche, droit et suprieur sont tests, la pile tant de longueur variable.
Lorsque la pile est slectionne, la carte du dessus est retourne si elle est
face cache. Sinon, on essaie de la dplacer vers une pile couleur, puis vers
une pile tableau. Sinon, an la laisse en place.
Pour afficher la pile, on dessine chaque carte ; on utilise une Enumeration
pour cela, disponible dans les classes dites de collection ( Vector, Stack,
BitSet, Hashtable, Properties).

4.8 Classe Solitaire


public class Solitare {
static public DeckPile deckPile;

82

Support de cours Java


static public
static public
static public
static public
private Frame

DiscardPile discardPile;
TablePile tableau [ ];
SuitPile suitPile [ ];
CardPile allPiles [ ];
window;

static public void main (String [ ] args) {


Solitare world = new Solitare();
}
public Solitare () {
window = new SolitareFrame();
init();
window.show();
}
public void init () {
// Dabord allouer les tableaux
allPiles = new CardPile[13];
suitPile = new SuitPile[4];
tableau = new TablePile[7];
// ensuite les remplir
allPiles[0] = deckPile = new DeckPile(335, 30);
allPiles[1] = discardPile = new DiscardPile(268, 30);
for (int i = 0; i < 4; i++)
allPiles[2+i] = suitPile[i] =
new SuitPile(15 + (Card.width+10) * i, 30);
for (int i = 0; i < 7; i++)
allPiles[6+i] = tableau[i] =
new TablePile(15 + (Card.width+5) * i,
Card.height + 35, i+1);
}
private class SolitareFrame extends Frame {
private class RestartButtonListener implements ActionListener {
public void actionPerformed (ActionEvent e) {
init();
window.repaint();
}
}
private class MouseKeeper extends MouseAdapter {

III.4 Exemple 3 : jeu de solitaire

83

public void
int
int
for

mousePressed (MouseEvent e) {
x = e.getX();
y = e.getY();
(int i = 0; i < 13; i++)
if (allPiles[i].includes(x, y)) {
allPiles[i].select(x, y);
repaint();
}

}
}

public SolitareFrame() {
setSize(600, 500);
setTitle("Solitaire Game");
addMouseListener (new MouseKeeper());
Button restartButton = new Button("New Game");
restartButton.addActionListener(new RestartButtonListener());
add("South", restartButton);
}
public void paint(Graphics g) {
for (int i = 0; i < 13; i++)
allPiles[i].display(g);
}
}
}

Constructeur :
cration dune fentre, SolitaireFrame,
appel de init(),
affichage de la fentre
init() ralise linitialisation des piles dclares static.
Notez : 3 oprations distinctes pour un tableau : dclaration, allocation
mmoire et assignation.
Exemple de suitPile[] :
Dclaration : static public SuitPile suitPile[] ;
Allocation : suitPile = new SuitPile[4] ; on rserve de la mmoire
pour les rfrences suitPile[0], . . . , suitPile[3] (c..d. n octets pour
les 4 pointeurs, en interne). Noter que le constructeur SuitPile() nest
pas encore appel.
Assignation :

84

Support de cours Java


for(int i = 0; i < 4; i++)
suitPile[i] = new SuitPile(15 + (Card.width+10) * i, 30);

Ici le constructeur est appel et les rfrences suitPile[0], . . . , suitPile[i]


sont assignes.
Classe interne SolitaireFrame sert grer la fentre de lapplication.
Des couteurs (listeners) sont crs pour un bouton et lvnement de clic
de souris.
Pressant le bouton, on rinitialise le jeu et on redessine la fentre.
Cliquant sur la souris, les piles sont examines et la pile approprie est
dessine.
On utilise ici le tableau allPiles[] ainsi que le principe de substitution :
allPiles[] est dclar comme un tableau de CardPile mais contient en fait
plusieurs varits de piles de cartes.
Ce tableau est utilis pour les comportements gnriques dune pile
de cartes. Par ex. dans paint(), chaque allPiles[i].display() pourra
appeler une mthode diffrente. Cest de la rpartition de mthode dynamique, un aspect du polymorphisme. Cela correspond aux mthodes
virtual du C ++.

IV Botes outils awt et Swing


Rfrences bibliographiques
Understanding Object Oriented Programming with Java, T. Budd [Bud98].

IV.1

Aperu des classes

1.1 Paquetages

java.awt : bases communes et composants de "1re gnration"


java.awt.event : gestion des vnements
javax.swing : alternative aux composants AWT
Les composants Swing ont un nom dbutant par J. Par exemple, Frame est
awt, JFrame est Swing.

1.2 Hirarchie des classes Swing


Object
Component
Container
JComponent
Applet
JApplet
Panel
Window
JWindow
Frame
JFrame
Dialog
JDialog

85

86

Support de cours Java

1.3 Classes Component, Container, Window


Une classe de base est la classe Frame. Elle hrite en fait d Object, de
Component, Container et Window.
Component : lment pouvant safficher sur un cran et avec lequel lutilisateur interagit.
Parmi les attributs : la taille, la position, des couleurs de fond et de premier
plan, le fait dtre visible ou non et un ensemble dcouteurs vnements.
Mthodes de Component :
methode()

But

setSize(int, int), getSize()


enable(), disable()
setLocation(int, int),
getLocation()
setVisible(boolean)
setForeground(Color),
getForeground()
setBackground(Color),
getBackground()
setFont(Font), getFont()
addMouseListener(MouseListener)

Fixer et obtenir la taille du composant.


Rendre un composant actif/inactif.
Fixer et obtenir la position du composant.

addKeyListener(KeyListener)
repaint(Graphics)
paint(Graphics)

Montrer ou cacher un composant.


Fixer et obtenir la couleur darrire-plan.
Fixer et obtenir la couleur de premier plan.
Fixer et obtenir la police de caractres.
Ajouter un couteur vnements de souris
pour le compoasnt.
Ajouter un couter vnement de touche
de clavier pour le composant.
Planifier le r-affichage dun composant.
R-afficher le composant.

Un Container est un composant qui peut en inclure dautres.


Parmi les mthodes de Container :
methode()

But

setLayout(LayoutManager)

Prparer le gestionnaire de positionnement


(layout manager) pour laffichage.
Ajouter ou enlever un composant de laffichage.

add(Component), remove(Component)

Une Window (fentre) est une surface 2 dimensions sur laquelle on peut
dessiner et qui peut tre affiche. Parmi les mthodes de Window :

IV.1 Aperu des classes

87

methode()

But

show()
toFront()
toBack()

Rendre la fentre visible.


Mettre la fentre au premier plan.
Mettre la fentre en arrire plan.

1.4 Classe Frame


Un Frame (chassis) est une fentre qui comporte une barre de titre, une
barre de menu, une bordure, un curseur et dautres proprits.
Parmi les mthodes de Frame :
methode()

But

setResizable()
setTile(String), getTitle()
setCursor(int)
setMenuBar(MenuBar)

Rendre la fentre taille variable.


Fixer ou obtenir le titre.
Fixer le curseur.
Fixer la barre de menu de la fentre.

Utilisation dans lexemple du flipper de la section 2.1, p. 56 de


methode()

But

setTitle(String)
setSize(int, int)
show()
paint()

Hrite
Hrite
Hrite
Hrite

de
de
de
de

Frame.
Component.
Window.
Component, redfini.

La puissance dAWT, comme celle de tout cadre logiciel, vient de lutilisation de variables polymorphes.
Lorsque la mthode show() de la classe Window est appele :
elle appelle setVisible() de la classe Component ;
cette dernire appelle repaint(),
qui appelle paint().
Le code de lalgorithme utilis par setVisible() et repaint() rside dans
la classe Component. Lorsquelle est excute, le cadre (AWT) pense quil
ne sagit que dun instance de Component.
En ralit, la mthode paint() qui est finalement excute est celle redfinie
dans la classe PinBallGame.

88

Support de cours Java


Le code des classes parent ( Component, Container, Window et Frame) est
donc en un sens gnrique.
Il suffit de redfinir la mthode paint() pour avoir un comportement spcifique lapplication.
La combinaison de lhritage, de la redfinition et du polymorphisme permet
une rutilisation grande chelle.

1.5 Classe JFrame

Fentre gnrale
Une classe hrite de JFrame contiendra le main() de lapplication
Contient une fille unique, de type JRootPane
Cette fille contient 2 filles :
layeredPane de type JLayeredPane, responsable des composants pouvant
tre empils les uns au dessus des autres
glassPane de type JPanel, responsable de la capture des vnements
souris
layeredPane a 2 filles :
MenuBar de type JMenuBar, responsable des composants menus
contentPane de type JPanel, responsable des autres composants
sous-classe de Frame de AWT
Fonctionne de la mme faon part quelle contient un un contentPane de
type JPanel
Tout composant (ou sous-Panel) c doit tre ajout la contentPane par
getContentPane().add(c)

Le gestionnaire de positionnement est celui de la contentPane ( BorderLayout


par dfaut) Il se modifie par getContentPane().setLayout()
Possibilit de dfinir un comportement par dfaut quand on la ferme (excut APRES les windowClosing() des WindowListener enregistrs) par
setDefaultCloseOperation()

1.6 Composants Usuels


JComponent : anctre de tous les composants Swing (sauf JApplet, JDialog,
JFrame)
JButton : bouton usuel
JCheckBox : case cochable ("indpendante")
JLabel : texte affich par le programme
JList : liste "scrollable" de choix
JTextField, JTextArea : pour entrer du texte
JPanel : conteneur de base pour grouper des composants

IV.1 Aperu des classes

89

JDialog : fentre secondaire ("esclave")


JMenu, JMenuBar, JMenuItem, JPopupMenu : pour les menus
JScrollBar : barre de dfilement
JScrollPane : pour donner une vue dun seul composant avec dfilement
JWindow : fentre sans barre de titre ni bordure

1.7 Conteneurs
classe abstraite Container (sous-classe de Component), avec sous-classes :
Panel : conteneur de base, pour grouper des composants Admet un analogue
Swing JPanel hritant de JComponent
ScrollPane : conteneur avec barres de dfilement, mais pour un seul composant Admet un analogue Swing JScrollPane hritant de JComponent
Window : fentre (sans bordure, ni titre), pour cration fentres personelles
par drivation Admet un analogue Swing JWindow hritant de Window
Frame : Fentre "usuelle" avec bandeau en haut Admet un analogue Swing
JFrame hritant de Frame
Dialog : fentre secondaire, associe une fentre matresse (notamment
pour pop-up de dialogue) Admet un analogue Swing JDialog hritant de
Dialog

FileDialog : fentre de slection de fichier

1.8 Composants spcifiquement Swing


JCheckBox : bote cocher permettant divers choix mutuellement
JRadioButton : sorte de JCheckBox, de forme circulaire, permettant des choix
mutuellement exclusifs via un ButtonGroup
JComboBox : liste droulante
JPasswordField : sorte de JTextField masquant les caractres taps (par
exemple pour saisie de mot de passe)
JTextPane : zone de texte ditable avec police de caractres et style
JSlider : curseur pour choisir graphiquement une valeur numrique
JToolTip : bulle daide
JProgressBar : barre davancement de tche
JTable : tableau (ditable) de donnes
JTree : reprsentation de donnes arborescentes (faon explorateur Windows)
JToolbar : barre doutils
JColorChooser : utilitaire pour choix de couleur
JFileChooser : utilitaire pour choix de ficher avec exploration possible

90

Support de cours Java

1.9 Conteneurs spcifiquement Swing


JOptionPane : boites de dialogue usuelles, que lon peut crer et afficher par
un simple appel de fonction :
JOptionPane.showMessageDialog() > message + 1 bouton OK
int r = JOptionPane.showConfirmDialog() > message + boutons style
Oui / Non / Annuler
int r = JOptionPane.showOptionDialog() > message + choix de boutons
String s = JOptionPane.showInputDialog() > message + zone saisie
texte + boutons OK / Annuler
JSplitPane : pour afficher deux composants cte cte (ou lun au-dessus
de lautre), et avec ligne de sparation dplaable par lutilisateur
JTabbedPane : regroupement de plusieurs composants accessibles via des
onglets
JFileChooser : fentre de slection de fichier ( ? FileDialog de AWT mais
en mieux)
JInternalFrame : pour faire des sous-fentres dans une fentre ("bureau
virtuel")

1.10 Classe JPanel


Conteneur trs gnral, drive directement de JComponent
Contient un FlowLayout par dfaut
Est opaque, ce qui importe pour les dessins

1.11 Classe JLayeredPane


Conteneur gnral pour des composants en couches.
On peut donner des valeurs de niveau aux composants indiquant qui est
affich au dessus.
Utilise le null Layout, donc positionner ses enfants avec setBounds().
Classe mre de JDesktopPane.

IV.2

Gestion des vnements

2.1 Gestion des vnements


Modle metteur/rcepteur, avec sparation claire entre
les lments dinterface qui mettent les vnements,

IV.2 Gestion des vnements

91

et des objets rcepteurs qui "coutent" les vnements et agissent en


consquence.
Classes dans le paquetage java.awt.event

2.2 Modle dvnements


Chaque composant peut gnrer des vnements (classe abstraite AWTEvent
et ses sous-classes MouseEvent, ActionEvent, ...)
Tout objet o qui doit ragir quand un type dvnement se produit dans
un certain composant c doit :
implanter linterface adquate ( MouseListener, ActionListener, ...)
tre enregistr dans la liste des objets intresss par ce type dvnements
issus de ce composant ( c.addMouseListener(o), c.addActionListener(o),
...)
quand un vnement se produit sur le composant c, il est transmis tous
les rcepteurs enregistrs chez lui pour ce type dvnment, ceci par appel de sa mthode correspondante, par ex., pour appui sur bouton souris,
o.mousePressed(evt) pour tous les o concerns, et o evt est lvnement

2.3 Types dvnements


Nom

Description

WindowEvent
ComponentEvent
FocusEvent

apparition, iconification, (d-)masquage, fermeture, ...


redimensionnement, dplacement, ...
dbut/fin de slection comme destinataire des entres (clavier et souris)
KeyEvent
clavier (touche appuye, ...)
MouseEvent
souris (boutons, dplacement, ...)
ActionEvent
appui sur JButton, double-clic sur item de JList, Return
dans un JTextField, choix dun JMenuItem, ...
ItemEvent
(d-)slection dune JCheckbox, dun item de JList, passage sur un JMenuItem, ...
TextEvent
modification de texte
ContainerEvent
ajout/suppression de composant
AdjustementEvent dfilement de JScrollbar

2.4 Sources dvnements


vnement

Source

92

Support de cours Java

Tous les Component


Toutes les Window

ComponentEvent,
FocusEvent,
KeyEvent, MouseEvent

MenuItem

WindowEvent, Button
ActionEvent

List

ActionEvent, ItemEvent

CheckBox, CheckBoxMenuItem, Choice

ItemEvent

TextField

ActionEvent, TextEvent

TextArea et autres, TextComponent

TextEvent

Tous les Container

ContainerEvent

2.5 Interfaces rcepteurs


A chaque classe XxxEvent est associe une interface XxxListener, qui regroupe
une ou plusieurs mthodes (lesquelles passent toutes lvnement en paramtre) :
vnement

Interface

Mthodes associes

KeyEvent

KeyListener

MouseEvent

MouseListener

MouseEvent

MouseMotionListener

ComponentEvent

ComponentListener

FocusEvent
WindowEvent

FocusListener
WindowListener

keyPressed(),
keyReleased(), keyTyped()
mouseClicked(),
mouseEntered(),
mouseExited(),
mousePressed(),
mouseReleased()
mouseDragged(),
mouseMoved()
componentHidden(),
componentMoved(),
componentResized(),
componentShown()
focusGained(), focusLost()
windowActivated(),
windowClosed(),
windowClosing(),
windowDeactivated(),
windowDeiconified(),
windowIconified(),
windowOpened()

IV.3 Gestionnaire de disposition


ActionEvent
ItemEvent
TextEvent
AdjustmentEvent
ContainerEvent

IV.3

ActionListener
ItemListener
TextListener
AdjustmentListener
ContainerListener

93
actionPerformed()
itemStateChanged()
textValueChanged()
adjustmentValueChanged()
componentAdded(),
componentRemoved()

Gestionnaire de disposition

3.1 Schma gnral


Un gestionnaire de disposition est responsable de la disposition des composants quil contient.
En gnral , lutilisateur choisit un gestionnaire parmi ceux disponibles et
ce dernier se dbrouille pour la disposition.
Chaque gestionnaire implante linterface LayoutManager.
On a en fait le schma suivant :
Lapplication hrite de Container. Cela lui permet de bnficier des mthodes de Window et den redfinir (par ex. paint()).
Container contient comme lun de ses champs le gestionnaire de disposition. En fait la variable correspondante est polymorphe et cest par ex.
un
GridLayout qui implante en fait linterface LayoutManager, de manire
transparente au Container.
Il y a donc combinaison dhritage, de composition et dimplantation dinterface.

3.2 Le gestionnaires BorderLayout


Il y a 5 types standard de gestionnaires : BorderLayout, GridLayout,
CardLayout, FlowLayout et GridBagLayout.
BorderLayout : gestionnaire par dfaut pour les classes drivant de Frame.
Ne peut contenir plus de 5 composant diffrents. Les 5 positions sont North,
South, East, West et Center.
Les composants prsents occupent toute la place disponible.
Pour ajouter un composant, on utilise add() avec pour 1 ier argument sa
position. Par ex. :
add("North", new Button("quit")) ;

94

Support de cours Java

3.3 Le gestionnaires GridLayout


Les composants sont organiss selon une grille rectangulaire, chaque composant ayant la mme taille.
On fournit au constructeur :
en 2 premiers paramtres les nombres de lignes et de colonnes de la grille ;
en 2 derniers paramtres lespace hrizontal et vertical insr entre les
composants.
Exemple :
Panel p = new Panel();
p.setLayout(new GridLayout(4, 4, 3, 3));
p.add(new ColorButton(Color.black, "black"));

3.4 Le gestionnaires FlowLayout


Au sein dun FlowLayout les composants sont disposs en ranges, de gauche
droite et de haut en bas.
Contrairement au cas dun GridLayout, les composants peuvent avoir des
tailles diffrentes.
Lorsquun composant ne peut pas tenir dans la ligne sans tre tronqu, une
nouvelle ligne est cre.
Gestionnaire par dfaut pour la classe Panel.

3.5 Le gestionnaires CardLayout


Dans un CardLayout, les composants sont empils verticalement (lun au
dessus de lautre), un seul composant tant visible la fois.
Chaque composant est repr laide dune chane de caractres lors de son
ajout par add().
Cette chane est ensuite utilise pour rendre un composant visible.
Exemple :
CardLayout lm = new CardLayout();
Panel p = new Panel(lm);
p.add("Un", new Label("Numero Un"));
p.add("Deux", new Label("Numero Dexu"));

Type le plus gnral de gestionnaire de disposition : GridBagLayout.


Gnration dune grille non uniforme de carrs et placement des composants
dans chacun des carrs.

IV.4 Composants dinterface utilisateur spcifiquement Awt

95

3.6 Application avec IHM


Ecrire une sous-classe de JFrame adapte lapplication dans son constructeur, Crer les sous-conteneurs et composants lmentaires, et installer chacun sa place dans son conteneur (mthode add()). Redfinir ventuellement la mthode paint(Graphics) pour y faire tout ce qui est dessin dans
le main() :
Crer une instance f de la sous-classe de Frame en question
dimensionner : f.setSize(w,h)
positionner : f.setLocation(x,y)
afficher la fentre : f.show()

IV.4

Composants dinterface utilisateur


spcifiquement Awt

4.1 tiquette
Tous les composants dinterface utilisateur, sauf les menus, sont des sous
classes de Component.
Une tiquette ou Label contient du texte qui est affich.
Exemple :
Label lab = new Label("score : 0 a 0");
add("South", lab);

Une tiquette ne rpond aucun vnement (clic de souris ou touche de


clavier).
Le texte dune tiquette peut tre chang via setText(String). Il peut tre
obtenu par getText().

4.2 Bouton
Un bouton est un composant souvent reprsent par une forme ronde et
rpondant aux interactions de lutilisateur.
On obtient une intercaction avec un bouton en attachant un objet ActionListener
(couteur actions) au bouton.
Lobjet ActionListener est averti lorsque le bouton est enfonc.
Button b = new Button("Fais le");
b.addActionListener(new doIt());
private class doIt implements ActionListener {
public void actionPerformed(ActionEvent e) {

96

Support de cours Java


...
}
}

Une technique utile est de combiner le bouton et lcouteur en une nouvelle


et mme classe. Par ex.
private class ColorButton extends Button implements ActionListener
{
private Color ourColor;
public ColorButton(Color c, String name)
{
super(name); // creation du bouton
ourColor = c; // sauvegarder la valeur de couleur
addActionListener(this); // on sajoute comme ecouteur
}
public void actionPerformed(EctionEvent e)
{
setFromColor(ourColor); // fixer la couleur du panneau central
}

Lobjet senregistre lui-mme comme un couteur actions sur boutons.


Lorsquenfonc, le bouton appelle la mthode ActionPerformed().
On peut mme dfinir une classe abstraite :
abstract class ButtonAdapter extends Button implements ActionListener
{
public ButtonAdapter(String name)
{
super(name);
addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{
pressed();
}
public abstract void presses();
}

qui est la fois un bouton et un couteur.


Pour crer un bouton en utilisant cette abstraction, le programmeur doit
crer une sous-classe et redfinir la mthode pressed(). Par exemple :

IV.4 Composants dinterface utilisateur spcifiquement Awt

97

Panel p = new Panel();


p.add(new ButtonAdapter("Quit"){
public void pressed() { System.exit(0); } });

4.3 Canevas
Un canevas ou Canvas est un composant ayant seulement une taille et la
possibilit dtre une cible pour des oprations de dessin.
Le canevas est souvent tendu (via jcextends) pour obtenir dautres types
de composants. Un exemple est la classe ScrollPane.

4.4 Ascenseur
Un ascenseur ou ScrollBar (en gnral un carr que lon peut faire glisser)
est utilis pour spcifier des valeurs entires dans un intervalle.
Il est affich dans une direction horizontale ou verticale.
On peut spcifier :
les bornes de lintervalle ;
lincrment de ligne (la distance dont lascenseur se dplace lorsque lon
clique sur lune de ses extrmits, souvent des petits triangles) ;
lincrment de page (la distance dont lascenseur se dplace lorsque lon
clique sur une partie darrire plan entre le carr glissant et lune de ses
extrmits).
Comme un bouton, une interaction avec lutilisateur est fournie en dfinissant un couteur qui est averti lorsque lascenseur est modifi.
Exemple dune classe ColorBar qui reprsente un ascenseur pour rgler
lintensit dune couleur. Le constructeur de la classe cr un ascenseur
vertical avec une valeur initiale de 40 et un intervalle gal [0,255].
private class ColorBar extends ScrollBar implements AdjustmentListener
{
public ColorBar(Color c)
{
super(ScrollBar.VERTICAL, 40, 0, 0, 255);
setBackground(c);
addAdjustmentListener(this);
}
public void adjustmentValueChanged(AdjustmentEvent e)
{
setFromBar(); // prendre la valeur de lascenseur en

98

Support de cours Java


//

utilisant getValue()

}
}

La couleur de fond est fournie en argument. Enfin lobjet est lui-mme un couteur
vnements dascenseur. Lorsque lascenseur est modifi, la mthode adjustmentValueChanged()
est excute.
Pour les 3 couleurs primaires, on prendra 3 ascenseurs et leur valeur sera obtenue
via la mthode setFromBar().

4.5 Composants texte


Un tel composant est utilis pour afficher du texte ditable.
2 formes :
TextField : bloc de taille fixe.
TextArea : utilise des ascenseurs pour afficher un bloc de texte plus gros
dont tout nest pas forcment visible en mme temps
Le texte peut tre fix ou obtenu via setText(String) et getText(). Du
texte supplmentaire peut tre rajout via append(String).
Un TextListener peut tre attach un composant de texte. Lcouteur
doit implanter linterface TextListener
interface TextListener extends EventListener {
public void textValueChanged(TextEvent e);
}

4.6 Botes de contrle


Une botes de contrle ou Checkbox est un composant qui maintient et
affiche un tat binaire. Cet tat peut tre soit on, soit off et une tiquette
afficahble y est associe.
Ltiquette et ltat de la bote peuvent tre fixs par le programmeur en
utilisant getLabel(), setLabel(), getState() et setState().
Changer ltat dun Checkbox cr un vnement ItemEvent, enregistr par
un couteur vnements ItemListener.
class ChackTest extends Frame
{
private Checkbox cb = new Checkbox("Le checkbox est off);
public static void main(String args[])
{

IV.4 Composants dinterface utilisateur spcifiquement Awt

99

Frame world = new CheckTest();


world.show();
}
public CheckTest()
{
setTitle("Exemple de checkbox");
setSize(300, 70);
cb.addItemListener(new CheckListener());
add("Center", cb);
}
private class CheckListener implements ItemListener
{
public void ItemStateChanged(ItemEvent e)
{
if(cb.getState())
cb.setLabel("Le checkbox est on");
else
cb.setLabel("Le checkbox est off");
}
}
}

4.7 Groupes de checkbox, choix et listes


3 types de composants pour choisir entre plusieurs possibilits :
Un groupe de checkbox connectes, une seule pouvant tre slectionne
la fois. Ceci se nomme parfois un groupe de boutons radio (fonctionnement rappelant celui des boutons de certains autoradois).
Un choix ou Choice qui naffiche quune slection la fois, mais lorsque
lutilisateur clique sur la zone de slection, un menu instantan (pop-up
menu) appart et permet deffectuer un choix diffrent.
Une liste ou List, similaire au choix, mais plusieurs possibilits parmi
celles disponibles peuvent tre affiches simultanment et un ascenseur
permet daller voir les autres possibilits.
class ChoiceTest extends Frame
{
public static void main(String args[])
{
Window world = new ChoiceTest();
world.show();

100

Support de cours Java


}
private String[] choices = { "Un", "Deux", "Trois", "Quatre",
"Cinq", "Six", "Sept", "Huit", "Neuf", "Dix" };
private Label display = new Label();
private Choice theChoice = new Choice();
private List theList = new List();
private CheckboxGroup theGroup = new CheckboxGroup();
private ItemListener theListener = new ChoiceListener();
public ChoiceTest()
{
setTitle("Example de selections");
setSize(300, 300);
for(int i = -; i < 10; i++)
{
theChoice.addItem(choices[i]);
theList.addItem(choices[i]);
}
theChoice.addItemListener(theListener);
theList.addItemListener(theListener);
add("West", makeCheckBoxes());
add("North", theChoice);
add("East", theList);
add("South", display);
}
private class ChoiceListener implements ItemListener
{
public void itemStateChanged(ItemEvent e)
{
display.setText( theGroup.getSelectedCheckboxGroup().getLabel() +
theList.getSelectedItem() + theChoice.getSelectedItem() );
}
}
private Panel makeCheckBoxes()
{
Panel p = new Panel(new GridLayout(5, 2));
for(int i = 0; i < 10; i++)
{
Checkbox cb = new Checkbox(choices[i], theGroup, false);
cb.addItemListener(theListener);

IV.4 Composants dinterface utilisateur spcifiquement Awt

101

p.add(cb);
}
return(p);
}
}

Un groupe de checkbox ne devrait tre utilis que lorsque le nombre dalternatives est petit.
Un choix ou une liste devrait tre utilis si le nombre dalternatives dpasse
5.
Pour crer un Choice ou une List, le programmeur spcifie chaque alternative en utilisant addItem().
Un ItemListener peut tre attach lobjet. Lorsquune slection est faite,
lcouteur en est inform via la mthode itemStateChanged(). Le texte de
lentre slectionne peut tre obtenu via getSelectedItem().
Pour structurer un groupe de checkbox, le programmeur cr dabord un
CheckboxGroup. Cette rfrence est ensuite passe comme argument chacune des checkbox cre avec un e argument indiquant si le checkbox doit
tre initialement actif. Le dernier checkbox slectionn peut tre obtenu en
utilisant getSelectedCheckbox().
Puisquun groupe de checkbox est construit avec plusieurs composants, il
est presque toujours dispos sur un panneau (ou Panel). Dans lexemple,
une grille 52 est utilise comme disposition pour les 10 checkbox.

4.8 Panneau
Un panneau Panel est un Container qui agit comme un Component. Il
repsente une rgion rectangulaire qui possde son proper gestionnaire de
disposition (qui peut tre diffrent de celui de lapplication).
Des composants peuvent tre insrs dans un panneau. Le panneau est alors
lui-mme insr comme une seule unit dans la fentre de lapplication.
Utilisation dun panneau dans lexemple prcdent. La mthode makeChekcboxes()
cre un panneau pour contenir 10 checkbox. Il est structur en utilisant un
GridLayout 52. Puis ce groupe de 10 lments est trait comme un unique
lment et est plac gauche de la fentre de lapplication.

4.9 Carreau dfilant


Un carreau dfilant ou ScrollPane est similaire un panneau, mais il ne
peut contenir quun lment. Et si cet lment est trop grand pour tre
visible dun seul coup, un ou 2 ascenseurs sont automatiquement gnrs
pour pouvoir se dplacer.

102

Support de cours Java


Exemple dune fentre de 300300 pixels avec un ScrollPane contenant un
Canvas de 10001000 pixels.

class BigCanvas extends Frame


{
public static void main(String args[])
{
Frame world = new BigCanvas();
world.show();
}
private Polygon poly = new Polygon();
private Canvas cv = new Canvas();
public BigCanvas()
{
setSize(300, 300);
setTitle("Test de ScrollPane");
cv.setSize(1000, 1000);
cv.addMouseListener(new MouseKeeper());
ScrollPane sp = new ScrollPane();
sp.add(cv);
add("Center", sp);
}
public void paint(Graphics g)
{
Graphics gr = cv.getGraphics();
gr.drawPolygon(poly);
}
private class MouseKeeper extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
poly.addPoint(e.getX(), e.getY());
repaint();
}
}
}

IV.5 Un exemple

IV.5

103

Un exemple

5.1 Affichage de couleurs


La classe ColorTest cre une fentre pour afficher des couleurs et leur valeur.
La fentre est divise en 4 rgions. Les rgions sont gres par le gestionnaire
par dfaut, de type BorderLayout.
En haut (rgion nord) se trouve une zone de texte, un composant de type
TextField qui dcrit la couleur courante.
gauche (rgion est) se trouve un trio dascenseurs qui peuvent tre utiliss
pour fixer les valeurs des canaux rouge, vert et bleu.
A droite (rgion ouest), il y a un banc 44 de 16 boutons. Ils sont construits
sur un Panel qui est gr par un gestionnaire GridLayout. 13 boutons reprsentent des couleurs prdfinies. 2 autres servent jouer sur la luminance.
Le dernier sort de lapplication.
Au milieu se trouve un panneau dans lequel on affiche la couleur.
La classe ColorTest contient 6 champs :
la couleur courante du panneau du milieu,
les 3 ascenseurs de rglage des couleurs primaires,
le champ de texte en haut,
le panneau (color) du milieu.
class ColorTest extends Frame
{
public static void main(String args[])
{
Frame window = new ColorTest();
window.show();
}
private
private
private
private
private
private

TextField colorDescription = new TextField();


Panel ColorField = new Panel();
Color current = Color.black;
ScrollBar redBar = new ColorBar(Color.red);
ScrollBar greenBar = new ColorBar(Color.green);
ScrollBar blueBar = new ColorBar(Color.blue);

public ColorTest()
{
setTitle("Test de couleurs");
setSize(400, 600);
add("North", colorDescription);
add("East", makeColorButtons);

104

Support de cours Java


add("Center", colorField);
add("West", makeScrollbars());
setFromColor(current);
}
private void setFromColor(Color c)
{
current = c;
colorField.setBackground(current);
redBar.setValue(c.getRed());
greenBar.setValue(c.getGreen());
blueBar.setValue(c.getBlue());
colorDescription.setText(c.toString());
}
private void setFromBar()
{
int r = redBar.getValue();
int g = greenBar.getValue();
int b = blueBar.getValue();
setFromColor(new Color(r, g, b));
}
private Panel makeColorButtons() { ... }
private Panel makeScrollBars() { ... }
private class BrightenButton extends Button implements ActionListener ...
private class ColorButton extends Button implements ActionListener ...
private class ColorBar extends ScrollBar implements AdjustmentListener ...
}

Les 3 ascenseurs utilisent la classe colorBar dcrite prcdemment. On


fournit au constructeur les couleurs du carr glissant et du fonc. Lors de
lappel de lcouteur, on invoque adjustmentValueChanged() qui appelle
setFromBar().
makeScrollBars() cre le panneau qui contient les 3 ascenseurs.
Chaque instance de ColorButton, dcrit prcdemment, hrite de la classe
Button et implante linterface ActionListener. Lorsque le bouton est enfonc, la mthode setFromColor() est appele pour mettre jour la couleur
du panneau central.
La classe BrightenButton utilise un indice ( index) qui indique si le bouton
augmente ou diminue la clart (clairer ou assombrir).
private class BrightenButton extends Button implements ActionListener

IV.5 Un exemple
{
private int index;
public BrightenButton(int i)
{
super(i == 0 ? "eclaire" : "assombrir");
index = i;
addEctionListener(this);
}
public void ActionPerformed(ActionEvent e)
{
if(index == 0)
setFromColor(current.brighter());
else
setFromColor(current.brighter());
}
}

Les 16 boutons sont dfinis par :


private Panel makeColorButtons()
{
Panel p = new Panel();
p.setLayout(new GridLayout(4,4,3,3));
p.add(new ColorButton(Color.black, "black"));
p.add(new ColorButton(Color.blue, "blue"));
p.add(new ColorButton(Color.cyan, "cyan"));
p.add(new ColorButton(Color.darkGray, "darkGray"));
p.add(new ColorButton(Color.gray, "gray"));
p.add(new ColorButton(Color.green, "green"));
p.add(new ColorButton(Color.lightGray, "lightGray"));
p.add(new ColorButton(Color.magenta, "magenta"));
p.add(new ColorButton(Color.orange, "orange"));
p.add(new ColorButton(Color.pink, "pink"));
p.add(new ColorButton(Color.red, "red"));
p.add(new ColorButton(Color.white, "white"));
p.add(new ColorButton(Color.yellow, "yellow"));
p.add(new BrightenButton(0));
p.add(new BrightenButton(1));
}

105

106

Support de cours Java

IV.6

Botes de dialogue

6.1 Descrition dun dialogue


Un Dialog est un type de fentre spcial affiche pendant une priode de
temps limite. Les dialogues sont souvent utiliss pour prvenir lutilisateur
de certains vnements ou pour poser des questions simples.
Les dialogues peuvent tre modaux ou non modaux. Un dialogue modal
exige une rponse de lutilisateur et empche ce dernier deffectuer une autre
action jusqu ce que le dialogue soit termin.
Un dialogue non modal peut tre ignor par lutilisateur. Les actions dun
dialogue non modal sont souvent places dans une action (thread) spare ;
de cette manire, les actions produites par le dialogue ne perturbent pas le
reste de lapplication.
Les 2 arguments dun constructeur de Dialog sont le Frame de lapplication
et une valeur boolenne qui est vraie si le dialogue est modal.
Dialog dial = new Dialog(this, false) ;

Un Dialog tant de type Window, des composants graphiques peuvent y


tre placs, comme dans Frame ou dans un Panel. Le gestionnaire de positionnement par dfaut est un BorderLayout.
La plupart des fonctions utilises par un Dialog sont hrites des classes
parentes. Parmi celles-ci
methode()

But

setSize(int, int)
show()
setVisible(false)
setTitle(String), getTitle()

Fixer la taille de la fentre


Afficher la fentre
Faire disparatre la fentre
Fixer ou obtenir le titre de la fentre.

Pour un dialogue modal, la mthode show() ne se termine que lorsque le


dialogue disparat. Il faut donc appeler un moment setVisible(false).

6.2 Exemple de dialogue


Exemple
class Dialog extends Frame
{
public static void main(String args[])
{
Frame world = new BigCanvas();

IV.6 Botes de dialogue


world.show();
}
private TextArea display = new TestArea();
private Chaeckbox cb = new Checkbox("Dialogue modal");
public DialogTest()
{
setTitle("Programme de test de dialogue");
setSize(300, 220);
add("West", cb);
add("East", new Makebutton());
add("South", display);
}
private class Makebutton extends ButtonAdapter
{
public Makebutton() { super("Creer le dialogue"); }
public void pressed() { makeDialog(cb.getState()); }
}
private void makeDialog(boolean modalFlag)
{
final Dialog dlg = new Dialog(this, modalFlag);
dlg.setSize(100, 100);
dlg.add("North", new CountButton(1));
dlg.add("West", new CountButton(2));
dlg.add("East", new CountButton(3));
dlg.add("South", new ButtonAdapter("Hide") {
public void pressed() { dlg.setVisible(false); }});
dlg.show();
}
private class CountButton extends ButtonAdapter
{
public CountButton(int val)
{
super("" + val);
}
public void pressed()
{
display.append("Bouton " + getLabel() + "presse\n");
}

107

108

Support de cours Java


}
}

Cration dune fentre avec un checkbox, un bouton et une zone de texte.


Le checkbox permet lutilisateur de spcifier si le dialogue crer sera
modal ou non. Le bouton permet de crer le dialogue, et la zone de texte
affiche quels sont les boutons enfoncs dans le dialogue.
La procdure makeDialog() cre la bote de dialogue. La taille de la bote
est fixe 100100 pixels et 4 boutons sont placs dans la bote. 3 des boutons affichent simplement une ligne de texte dans la fentre prcdente. Le
dernier cache le dialogue. Dans le cas dun dialogue modal, acher le dialogue
quivaut le faire diparatre, et le contrle est renvoy la procdure qui a
cr le dialogue.

IV.7

Menus

7.1 Barre de menus


Une barre de menus nest pas une sous classe de Component, puisque les
plate formes diffrent dans la manire de grer les barres de menus.
Une barre de menus contient des menus qui contiennent une suite dentres. Une instance de barre de menus peut tre attache un Frame via
setMenuBar() :
MenuBar bar = new MenuBar();
setMenuBar(bar);

Les divers menus sont nomms et ajouts la barre de menus via add() :
Menu helpMenu = new Menu("Aide");
bar.add(helpMenu);

Des entres de menus sont cres en utilisant la classe MenuItem. Chaque


entre de menu maintient une liste dobjets ActionListener. Ces couteurs
seront avertis lorsque lentre de menu est slectionne
MenuItem quitItem = new MenuItem("Quitter");
quitItem.addActionListener(new QuitListener());
helpMenu.add(quitItem);

On peut galement crer des menus de type particulier.

IV.7 Menus

109

7.2 Un utilitaire de menu quitter


La classe QuitItem cre un couteur qui va arrter lapplication lorsque
lentre de menu correspondante sera slectionne.
En surchargeant le constructeur, on peut ajouter trivialement cet utilitaire
nimporte quelle application
class QuitItem implements ActionListener
{
public QuitItem(Frame application)
{
MenuBar mbar = new MenuBar();
application.setMenuBar(mbar);
Menu menu = new Menu("Quitter");
mbar.add(menu);
MenuItem mitem = new MenuItem("Quitter");
mitem.addActionListener(this);
menu.add(mitem);
}
public QuitItem(MenuBar mbar)
{
Menu menu = new Menu("Quitter");
mbar.add(menu);
MenuItem mitem = new MenuItem("Quitter");
mitem.addActionListener(this);
menu.add(mitem);
}
public QuitItem(Menu menu)
{
MenuItem mitem = new MenuItem("Quitter");
mitem.addActionListener(this);
menu.add(mitem);
}
public QuitItem(MenuItem mitem)
{
mitem.addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{
System.exit(0);

110

Support de cours Java


}
}

Le constructeur de QuitItem peut avoir comme argument :


un MenuItem
un Menu, auquel cas une entre tiquete "Quitter" est cre.
un MenuBar ; un menu est alors cr, tiquet "Quitter", ainsi quune
entre de menu.
un Frame (lapplication entire), auquel cas, une barre de menus, un menu
et une entre de menu sont crs.

V Classe Url
Rfrences bibliographiques
Java et Internet Concepts et programmation, G. Roussel, E. Duris, N.
Bedon et R. Forax [RDBF02],
Java Network Programming, E.R. Harold [Har97].

V.1

Classe URL

1.1 Gnralits
Un URL (Uniform Resource Locator ou localisateur de ressource uniforme) est un schma de nommage et daccs des objets sur le rseau.
L objet est de fournir une interface simple afin dextraire des objets du
rseau. Un URL consiste en 6 champs : le protocole, le nom de dhte (ou
de machine), le numro de port, le chemin, le nom de fichier et la section.
Exemple : http ://sunsite.unc.edu :8080/pub/sound/monkey.au. On a ici :
le protocole : http
le nom dhte : sunsite.unc.edu
le numro de port : 8080
le chemin de fichier : /pub/sound/
le nom de fichier : monkey.au.
Syntaxe gnrale :
protocole ://nom-machine :no-port/chemin/nom-fichier#section

Informations sur les URLs :


http ://www.ncsa.uiuc.edu/demoweb/url-primer.html

La classe URL permet dobtenir des fichiers HTTP, des fichiers URLs et
certains types multimdias.
Il y a en fait six classes et interfaces :
La classe URL, pour rapatrier des URLs du rseau.
La classe URLConnection
111

112

Support de cours Java

1.2 Constructeurs
La classe URL reprsente un URL, comme utilis sur le Web.
URL(String url) throws MalformedURLException
Cr un objet de type URL partir dune spcification textuelle, typiquement
de la forme http ://www.nas.gov/index.html
URL(String protocol, String host, String file) throws MalformedURLException
Ce constructeur suppose un numro de port par dfaut de lURL. Ce numro
dpend du protocole et doit tre gr par un gestionnaire de protocole.
URL(String protocol, String host, int port, String file) throws MalformedURLException
Ce constructeur cre un URL de toutes pices. Le protocole est typiquement
http ou ftp.
URL(String protocol, String host, int port, String file, URLStreamHandler
handler) throws MalformedURLException

Ce constructeur cre un URL de toutes pices avec un gestionnaire spcifique de flux dURL, de type URLStreamHandler. Cest le mcanisme recommand pour que des applets du JDK 1.2 utilisent des gestionnaires de
protocole spcifiques.
URL(URL context, String relative)
Cre un URL partir dun URL existant context et dun URL textuel relatif relative. Par exemple, si context est http ://serveur/ et relative est
document.html, lURL cre correspondra http ://serveur/document.html.
Si relative est un URL absolu (commenant par le champ de protocole,
par ex. http :), le rsultat est juste un URL correspondant relative.
Les applets se serviront souvent de ce constructeur, utilisant le rsultat de
getCodeBase() comme contexte.
URL(URL context, String relative, URLStreamHandler handler) throws MalformedURLException
Cre un URL partir dun URL existant context et dun URL textuel
relatif relative avec un gestionnaire spcifique de flux dURL, de type
URLStreamHandler. Par dfaut, le nouvel URL hrite dun URLStreamHandler
de context. Cest le mcanisme recommand pour que des applets du JDK
1.2 utilisent des gestionnaires de protocole spcifiques.

1.3 Mthodes et exception


Les mthodes suivantes parmettent de dissecter un URL et den obtenir les
diffrents champs.
String getProtocol()
Renvoie le champ protocole de lURL. Ce sera par ex. http, ftp, mailto,
etc.

V.1 Classe URL

113

String getHost()
Renvoie le champ nom dhte de lURL.
int getPort()
Renvoie le champ numro de port de lURL.
String getFile()
Renvoie les champ chemin et nom de fichier de lURL.
String getRef()
Renvoie le partie rfrence (ou section) de lURL, si elle est prsente (partie
suivant le signe #).
boolean sameFile(URL other)
Renvoie true si lURL est gale other en ignorant le champ rfrence
(ou section) si elle est prsente.
String toExternalForm()
Renvoie une reprsentation sous forme de String de lURL, par ex. http ://x.y.z :80/fi
URLConnection openConnection() throws IOException
ouvre un socket pour lURL spcifi et renvoie un objet de type OpenConnection.
Ce dernier reprsente une connexion ouverte une ressource rseau. Cette
mthode est utilise lorsque lon veut communiquer directement avec le serveur. Lobjet de type URLConnection donne accs tout ce qui est envoy par
le serveur ; en plus du document lui-mme, sous forme brute (HTML, texte
ou binaire), lon voit tous les en-ttes requis par le protocole en cours dutilisation. Par exemple, si lon rapatrie un document HTML, GetConnection()
donnera accs au en-ttes HTTP suivi du flux HTML brut.
InputStream openStream() throws IOException
Cette mthode ouvre une connexion pour lURL correspondant et renvoie
un objet de type InputStream afin de lire son contenu. Les donnes obtenues sont de type brut (donnes ASCII pour un fichier texte, code HTML
pour un fichier HTML, etc.) et ne contiennent pas les en-tte de protocoles ;
pour les obtenir, se servir de openConnection(). Dans le cas du protocole HTTP, un appel openStream() envoie automatiquement une requte
HTTP et traite les en-tte associs. Cet appel est quivalent un premier
appel openConnection() suivi dun appel getInputStream() de la classe
URLConnection.
Object getContent() throws IOException
Cette mthode rcupre le contenu de lURL et renvoie une rfrence un
type Object. Le type rel de lobjet dpend du contenu de lURL. Si lURL
est une image et quun gestionnaire appripropri est disponible, un objet
de type ImageProducer est effectivement renvoy ; si le fichier est textuel
et quun gestionnaire est disponible, une objet de type String est renvoy.
Cet appel est un raccourci de lappel openConnection().getContent().
protected void set(String protocol, String host, int port, String file,

114

Support de cours Java


String ref)

Permet une sous-classe dURL de changer les valeurs des diffrents champs :
protocole, nom dhte, chemin et nom de fichier, rfrence.
static void setURLStreamHandlerFactory(URLStreamHandlerFactory factory)
Cette mthode fixe le URLStreamHandlerFactory global pour lapplication
courante. Cette mthode est utilise lorsque lon cherche dvelopper un
gestionnaire de protocole spcifique. Le gestionnaire factory doit tre capable de renvoyer un gestionnaire appropri pour les types de protocoles
URL envisags. Cette mthode ne peut tre appele quune fois. Ceci dit,
lenvironnement Java inclut des gestionnaires de protocoles par dfaut,
contenant au moins HTTP. Laccs cette mthode est restreint par le
gestionnaire de scurit en vigueur. Des applets non autorises ne peuvent
appeler cette mthode. Pour cette raison, lextension (au sens de lhritage)
de la hirarchie de URL nest utile que pour des applications. Les applets
doivent spcifier un gestionnaire, de type URLStreamHandler, pour chaque
URL quelles construisent.
Une Exception : MalformedURLException
Indique un URL ayant des champs incorrects. Sous-classe de IOException.
Exemple de chargement dune page HTML :
URL url = new URL("http://java.sun.com/index.html");
InputStream in = url.openStream();
Reader reader = new InputStreamReader(in, "latin1");
BufferedReader bufferedReader = new BufferedReader(reader);
PrintWriter console = new PrintWriter(System.out);
String line;
while ((line = bufferedReader.readLine()) != null)
console.println(line);
console.flush();
bufferedReader.close();

VI Paquetage java.net : Sockets


Rfrences bibliographiques
Java et Internet Concepts et programmation, G. Roussel, E. Duris, N.
Bedon et R. Forax [RDBF02],
Java Network Programming, E.R. Harold [Har97].

VI.1

Sockets : Rappels TCP/IP

1.1 Socket logique ; numro de port


Une socket logique est une paire (@IP, no port). Un numro de port est
ncessairement compris entre 1 et 65 535. Un numro de port sert dsigner
deux choses :
Au sein de la machine du client et dans la socket logique contenant
ladresse du serveur (pour sadresser ce dernier), le numro de port
dsigne le numro du service auquel le client veut sadresser.
Au sein du serveur et dans la socket logique contenant ladresse du client
(pour sadresser ce dernier), le numro de de port sert distinguer les
diffrents clients (pour le cas o plusieurs clients de la mme machine
veulent sadresser au mme serveur).
Lidentification unique sur reseau se ralise au moyen dune socket logique
et dun numro de protocole.
Ceci fournit la rponse aux questions :
O ALLER (@ Internet),
Quoi FAIRE ou encore QUI contacter (numro de port),
Comment y aller (numro de protocole).
115

116

Support de cours Java

VI.2

Sockets : Clients

2.1 Classe InetAdress


Abstraction dune adresse IP = portage transparent sous IPv6 (de 32
128 bits)
Pas de constructeur. Cration par getByName(), getLocalHost(), getAllByName().
Mthodes statiques : InetAddress getByName(), InetAddress getLocalHost(String
host), InetAddress[] getAllByName(String host).
InetAddress getLocalHost() throws UnkownHostException. Renvoie un
InetAddress correspondant la machine locale, ventuellement 127.0.0.1
(loopback, par ex. derrire un firewall).
InetAddress getByName(String host) throws UnkownHostException. Renvoie un InetAddress correspondant lhte spcifi en argument. Il
peut tre spcifi par nom (par ex. proxy0.att.com ou par adresse IP
127.0.0.1. Pas dautre moyen dun client pour construire une InetAddress
pour un hote distant.
InetAddress[] getAllByName(String host) throws UnkownHostException.
Renvoie un taleau d InetAddress correspondant lensemble des adresses
IP connues de lhte spcifi en argument. Ces machines diposent de plusieurs cartes (interfaces) ; cest par exemple le cas de routeurs.
Mthodes dinstance : byte[] getAddress(), String getHostName(), String
getHostAddress(), boolean isMulticastAddress()
byte[] getAddress() Renvoie un tableau de byte contenant ladresse
IP en network byte order (octet de poids fort dabord). Pour IPv4, 4
octets.
String getHostName() Renvoie le nom de lhte correspondant lobjet
InetAddress appelant. Si le nom nest pas dja connu, une requte DNS
est lance. Peut chouer derrire des firewalls.
String getHostAddress() Renvoie ladresse IP de lhte correspondant
lobjet InetAddress appelant sous forme quadri-octet (par ex. 127.0.0.1)
boolean isMulticastAddress() Teste si lobjet InetAddress correspondant reprsente une adresse multidiffusion (multicast) ; cest--dire si le
premier octet de ladresse est compris entre 224 et 239 (inclus).
Exceptions : UnknownHostException, SecurityException
UnknownHostException Sous-classe de IOException indiquant que lhte
na pu tre identifi.
SecurityException Leve si le SecurityManager interdit une opration.
Par ex., une applet gnrale ne peut construire une InetAddress du nom
du serveur Web dont elle est originaire.
Exemple : Attend en entre des noms dhte et affiche leur adresse IP.

VI.2 Sockets : Clients

117

import java.net.*;
import java.io.*;
public class InetExample {
public static void main (String args[]) {
printLocalAddress ();
Reader kbd = new FileReader (FileDescriptor.in);
BufferedReader bufferedKbd = new BufferedReader (kbd);
try {
String name;
do {
System.out.print ("Enter a hostname or IP address: ");
System.out.flush ();
name = bufferedKbd.readLine ();
if (name != null)
printRemoteAddress (name);
} while (name != null);
System.out.println ("exit");
} catch (IOException ex) {
System.out.println ("Input error:");
ex.printStackTrace ();
}
}
static void printLocalAddress () {
try {
InetAddress myself = InetAddress.getLocalHost ();
System.out.println ("My name : " + myself.getHostName ());
System.out.println ("My IP : " + myself.getHostAddress ());
} catch (UnknownHostException ex) {
System.out.println ("Failed to find myself:");
ex.printStackTrace ();
}
}

static void printRemoteAddress (String name) {


try {
System.out.println ("Looking up " + name + "...");
InetAddress machine = InetAddress.getByName (name);
System.out.println ("Host name : " + machine.getHostName ());
System.out.println ("Host IP : " + machine.getHostAddress ());
} catch (UnknownHostException ex) {

118

Support de cours Java


System.out.println ("Failed to lookup " + name);
}
}
}

Notez quaprs un System.out.print(), on vide ( flush()) System.out, car


la mthode ne le fait pas automatiquement.

2.2 Sockets clientes : Classe Socket


Constructeurs
Crer une socket tablit automatiquement une connexion lhte et au
port spcifis.
Il doit y avoir un serveur en coute sur ce port.
Sinon, une IOException est leve (correspondant une connexion refused).
Socket(String host, int port) throws IOException Cre une socket et
la connecte au port spcifi et lhte spcifi. Lhte est spcifi par
son nom ou son adresse IP, le numro de port doit tre entre 1 et 65 535.
Socket(InetAddress address, int port) throws IOException Cre une
socket et la connecte au port spcifi et lhte spcifi par address. Le
numro de port doit tre entre 1 et 65 535.
Socket(String host, int port, InetAddress localAddress, int localPort)
throws IOException Cre une socket, lattache ladresse localAddress
et au port localPort, puis la connecte au port port et lhte host. Si
localAddress est 0, ladresse locale par dfaut est utilise. Si localPort
est 0, un numro de port alatoire non attribu est utilis.
Chaque connexion TCP consiste en deux paires (adresse IP locale, numro
de port local) et (adresse IP distante, numro de port distant). Lorsquune
Socket est cre et connecte un hte distant, on lui alloue usuellement
un numro de port non attribu de manire alatoire.
Spcifier le numro de port local peut tre utile pour des serveurs rclamant une connexion partir dun numro de port particulier ou bien en
cas de prsence dun firewall. Spcifier ladresse locale peut tre utile si
lhte a plusieurs adresses (plusieurs interfaces) et que lon souhaite en
choisir une pour des raisons de performances par exemple.
Socket(InetAddress address, int port, InetAddress localAddress, int
localPort) throws IOException Cre une socket, lattache ladresse
localAddress et au port localPort, puis la connecte au port port et
lhte dadresse address. Si localAddress est 0, ladresse locale par
dfaut est utilise. Si localPort est 0, un numro de port alatoire non
attribu est utilis.

VI.2 Sockets : Clients

119

Mthodes
Pour communiquer dun client au travers dune connexion TCP, il faut
crer une Socket, puis utiliser getInputStream() et getOutputStream()
pour communiquer avec le serveur distant.
InputStream getInputStream() throws IOException Cette mthode renvoie un InputStream, qui permet une communication par flux le long de la
connexion TCP. Pour des raisons defficacit, il est louable de bufferiser
les flux de Sockets.
InputStream getOutputStream() throws IOException Cette mthode renvoie un InputStream, qui permet une communication par flux le long de
la connexion TCP. Pour des raisons defficacit, il est louable de bufferiser les flux de Sockets. Dans le cas contraire, des transmissions dun
seul octet seront frquentes . . .
void close() thows IOException Cette mthode ferme lobjet Socket,
librant toute ressource systme et rseau utilise. Toute donne envoye
avant cet appel arrivera destination, sauf en cas de panne ou congestion rseau. Si l OutputStream de la Socket a t bufferis, lobjet
BufferedOutputStream devrait tre pralablement ferm, sous peine de
perdre les donnes contenues dans le tampon. Fermer lobjet Socket, celui InputStream ou celui OutputStream fermera la connexion rseau. Pas
de possibilit de fermeture partielle de connexion.
InetAddress getInetAddress() Renvoie ladresse IP de lhte distant.
int getPort() Renvoie le numro de port de lhte distant laquelle
lobjet Socket est connect.
InetAddress getLocalAddress() Renvoie ladresse locale ` laquelle lobjet Socket est attach ; cest linterface IP locale travers laquelle les
paquets sont envoys.
int getLocalPort() Renvoie le numro de port local auquel lobjet Socket
est attach. Cette valeur est assigne alatoirement si elle nest pas fournie
au constructeur.
void setSocketTimeout(int timeout) throws SocketException Fixe un
temps dexpiration, en milisecondes, au del duquel une lecture bloquante
sur cet objet Socket sera automatiquement arrte. Une valeur gale 0
rend nouveau les lectures bloquantes. Dans le cas darrt dune lecture,
une exception InterruptedIOException est leve.
int getSocketTimeout(int timeout) throws SocketException Renvoie
le temps dexpiration dune lecture. Une valeur gale 0 indique que les
lectures sont bloquantes.
void setTcpNoDelay(boolean on) throws SocketException Permet de
dactiver lutilisation de lalgorithme de Nagle sur une connexion socket.
Cet algorithme est utilis afin de rendre TCP plus efficace en retardant

120

Support de cours Java

lcriture de petites quantits de donnes jusqu ce que soit suffisamment de donnes aient t bufferises pour quun gros paquet puisse
tre envoy, soit toutes les donnes prtes tre envoyes au serveur sont
des accuss de rception. Pour des applications normales, lalgorithme
de Nagle accrot significativement les parformances, sauf pour certaines
applications bien particulires.
boolean getTcpNoDelay() throws SocketException Teste si lalgorithme
de Nagle est dsactiv. Renvoie false par dfaut.
void setSocketLinger(boolean on, int val) throws SocketException Permet un client de fixer un temps dexpiration maximum dune socket
TCP. Le protocole TCP garantit un acheminement sr des donnes transmises une socket. Ceci implique que lorsquune socket est ferme, la
connexion rseau ne se termine pas immdiatement ; elle reste ouverte le
temps que toutes les donnes soient achemines avec accus de rception.
Fixer un temps dexpiration signifie que le systme dexploitation nattendra que la priode spcifie aprs la fermeture dune socket avant de
fermer la connexion rseau. Si des donnes nont pas t transmises avec
succs durant cette priode, la connexion rseau est interrompue.
Lorsquune connexion rseau est ferme naturellement, il y a habituellement une priode de 4 minutes pendant laquelle une connexion identiqu ne peut tre recre (du mme port client au mme port serveur).
Ceci sert se protger contre les duplications involontaires de paquets ;
certains paquets peuvent tre retards dans certaines parties du rseau
et retransmis par la source. Mettre une temporisation supprimera cette
protection. Si une ancienne connexion est interrompue et une nouvelle
connexion identique est tablie avant que le retard de paquet soit coul,
le paquet en retard peut arriver et sinsrer dans la conversation. Cest
pourquoi, il est prfrable, en dehors de cas trs particuliers, dviter les
temps dexpiration de connexion.
int getSocketLinger() throws SocketException Renvoie le temps dexpiration d la connexion en cours ou 1 si loption est dsacive.
void setSendBufferSize(int size) throws SocketException Cette mthode demande au systme dexploitation de fixer le tampon denvoi (
SO_SNDBUF) size. Le systme dexploitation peut ignorer la requte . . .
int getSendBufferSize() throws SocketException Cette mthode renvoie la taille du tampon denvoi.
void setRecieveBufferSize(int size) throws SocketException Cette
mthode demande au systme dexploitation de fixer le tampon de rception ( SO_RCVBUF) size. Le systme dexploitation peut ignorer la
requte . . .
int getRecieveBufferSize() throws SocketException Cette mthode

VI.2 Sockets : Clients

121

renvoie la taille du tampon de rception.


Exceptions. Il y a 2 exceptions principales :
SocketException, fille de IOException et superclasse de diverses exceptions communes pour les sockets. Les sous-classes sont :
BindException Indique quun attachement au port ou ladresse locale
na pas pu se faire. Ceci arrive typiquement lorsque le port en question
est en cours dutilisation ou est un port systme, ou lorsque ladresse
locale spcifie nest pas celle dune interface rseau valide.
ConnectException Indique que la connexion a t refuse cause de
labscence de serveur en coute sur le port spcifi de la machine distante.
NoRouteToHostException Indique que la machine distante na pu tre
atteinte, typiquement cause dun problme rseau ou dun firewall.
SecurityException. Le gestionnaire de scurit ( SecurityManager) restreint la cration des Sockets. Une applet non autorise, par ex., ne
peut ouvrir des sockets sur une autre machine que sur celle de laquelle
elle a t lance. Un autre exemple notable est quune applet derrire un
firewall peut ne pas pouvoir faire de recherche (lookup) DNS ; il peut
donc tre ncessaire dutiliser des adresses IP numriques.
Exemple de client echo
Ce client cre un objet Socket pour tablir la connexion avec le serveur.
Le client lit les lignes sur lentre standard, les transmet au serveur, lit
la rponse du serveur et lcrit sur la sortie standard. Le client sarrte
lorsquil dtecte une fin de fichier sur lentre standard ou sur le flot
dentre du serveur.
Afin de lire les lignes de texte provenant du serveur, il cre un objet DataInputStream partir de lobjet inputStream fourni par lobjet
Socket. Il cre de mme un objet DataOUtputStream.
Ce client sexcute avec le nom du serveur sur la ligne de commande et,
de manire optionnelle, le numro de port utiliser.
import java.io.*;
import java.net.*;

public class EchoClient {


// Variables de classe constantes : port et machine par defaut
public static final int
DEFAULT_PORT = 6789;
public static final String DEFAULT_HOST = "localhost";
public static final int
DEFAULT_LINGER_TIME = 5;

122

Support de cours Java


// Variables dinstance (etat de EchoClient)
String host
= null; // Machine du serveur
int
port;
// No de port du serveur (no de service)
int
lingerTime;
// Tps dexpiration de socket apres fermeture
Socket s
= null; // Reference a la socket client
DataInputStream sin = null; // Flux en provenance du serveur
PrintStream sout
= null; // Flux a destination du serveur
DataInputStream in = null; // Flux associe au clavier
//----------------------------------------------------// METHODE main() : POINT DENTREE DU PROGRAMME CLIENT
//----------------------------------------------------public static void main(String[] args) {
// Le client
EchoClient client = null;
// Tests darguments et appel de constructeurs
switch(args.length) {
case 0 : client = new EchoClient();
break;
case 1 : if (args[0].equals("?")) {
usage();
System.exit(0);
}
client = new EchoClient(args[0]);
break;
case 2 : client = new EchoClient(args[0], args[1]);
break;
case 3 : client = new EchoClient(args[0], args[1], args[2]);
break;
default : usage();
throw new IllegalArgumentException();
}// switch()
// Creation dobjets utilitaires et traitement du service
client.initSocketAndStreams();
client.service();
}// main()

//---------------

VI.2 Sockets : Clients


// CONSTRUCTEURS
//--------------public EchoClient() {
host
= new String(DEFAULT_HOST);
port
= DEFAULT_PORT;
lingerTime = DEFAULT_LINGER_TIME; // Temps dexpiration
}
public EchoClient(String theHost) {
host = new String(theHost);
try {
// Test de validite de ladresse
InetAddress dummy = InetAddress.getByName(theHost);
} catch (UnknownHostException e) {
System.err.println("Machine " + theHost + " inconnue.");
System.err.println("Essai avec " + DEFAULT_HOST);
host = new String(DEFAULT_HOST);
}
port
= DEFAULT_PORT;
lingerTime = DEFAULT_LINGER_TIME;
}
public EchoClient(String theHost, String thePortString) {
this(theHost);
try {
port = Integer.parseInt (thePortString);
} catch (NumberFormatException e) {
System.err.println("Numero de port invalide. " +
DEFAULT_PORT + " Utilise.");
port = DEFAULT_PORT;
}
lingerTime = DEFAULT_LINGER_TIME;
}
protected EchoClient(String theHost, String thePortString,
String theLingerTimeString) {
this(theHost, thePortString);
try {
lingerTime = Integer.parseInt (theLingerTimeString);
} catch (NumberFormatException e) {
lingerTime = DEFAULT_LINGER_TIME;
}
}

123

124

Support de cours Java

//--------------------------------------------// FONCTIONS DUTILISATION ET DINITIALISATION


//--------------------------------------------static void usage() {
System.out.println("Utilisation : " +
"java EchoClient <hostname> [<port>] [<tempsExpir>]");
System.exit(0);
}
public void initSocketAndStreams()
{
try {
// Creer une socket pour se connecter a la machine et au port
s = new Socket(host, port);
// Mise du temps dexpiration a la valeur specifiee
// Voir la mise en garde dans le polycopie en cas reel
if (lingerTime > 0)
s.setSoLinger(true, lingerTime);
else
s.setSoLinger(false, 0);
// Creer les flux pour lire et ecrire des lignes de texte
// de et vers cette socket.
sin = new DataInputStream(s.getInputStream());
sout = new PrintStream(s.getOutputStream());
// Creer un flux pour lire des lignes de lentree standard
//
(par defaut le clavier)
in
= new DataInputStream(System.in);
// Signaler que lon est connecte
System.out.println("Connecte a " + s.getInetAddress()
+ ":"+ s.getPort());
} catch (IOException e) {
System.err.println(e);
try { if (s != null) s.close(); } catch (IOException e2) { ; }
}
}

//---------------------// TRAITEMENT DU SERVICE

VI.3 Sockets serveurs

125

//---------------------public void service() {


try {
String line;
while(true) {
// afficher une invite
System.out.print("EchoClient > ");
System.out.flush();
// lire une ligne de stdin; verifier lEOF
line = in.readLine();
if ((line == null) || (line.equals("quit"))) break;
// Lenvoyer au serveur
sout.println(line);
// Lire une ligne du serveur
line = sin.readLine();
// Verifier si la connexion est fermee (par EOF)
if (line == null) {
System.out.println("Connection fermee par le serveur.");
break;
}
// Puis ecrire la ligne sur stdout
System.out.println(line);
}// while(true)
} catch (IOException e) { System.err.println(e); }
// Etre sur de toujours fermer la socket
finally {
try { if (s != null) s.close(); } catch (IOException e2) { ; }
}
}// service()
}// class EchoClient

VI.3

Sockets serveurs

3.1 Classe ServerSocket


Schma simple (serveur itratif) :
Un serveur prend un numro de port et se met en coute de connexions ;
lorsquun client se connecte, le serveur reoit une objet Socket afin de
communiquer avec le client ;
lchange peut commencer.

126

Support de cours Java


Schmas plus complexes :
Serveurs concurrents : multi-activits (multi-threaded).
Communications inter-clients via le serveur.
Scurisation, authentification.
Constructeurs
Les objets ServerSocket sont construits en choisissant un numro de
port sur la machine locale (compris entre 1024 et 65 535). Si le numro de
port est 0, le systme dexploitation selectionnera un numro arbitraire
dun port libre chaque lancement de lapplication. Le numro doit alors
tre communiqu aux clients par un autre moyen. Un serveur doit explicitement accepter (par la mthode accept() une connexion dun objet
ServerSocket pour obtenir un objet Socket de connexion avec un client.
Le systme dexploitation commencera accepter les connexions ds la
cration de lobjet ServerSocket. Ces connexions seront places dans une
file dattente et seront retires une une chaque appel d accept(). Un
constructeur permet de spcifier combien de connexions il est possible de
mettre dans la file dattente. Si la file est pleine, toute autre connexion
est refuse par le systme dexploitation.
ServerSocket(int port) throws IOException Construit un objet ServerSocket
en coute sur le port numro port de la machine locale. La valeur par
dfaut de connexions pendantes (mises en file dattente) est de 50. Le
systme dexploitation acceptera donc au plus 50 clients dans la file des
clients en attente. Toute tentative de connexion au-del de cette limite
sera refuse. Notez bien quil ne sagit pas du nombre maximum de clients
que le serveur est capable de traiter simultanment, mais du nombre de
connexions en attente dans le cas o le serveur est lent en acceptation de
nouvelles connexions.
ServerSocket(int port, int backlog) throws IOException Construit
un objet ServerSocket en coute sur le port numro port de la machine
locale. La valeur backlog spcifie le nombre de connexions pendantes
que le systme dexploitation met en file dattente.
ServerSocket(int port, int backlog, InetAddress bindAddress) throws
IOException Construit un objet ServerSocket en coute sur le port numro port de la machine locale avec au maximum backlog connexions
pendantes. Cet objet ServerSocket nacceptera de connexions qu ladresse
locale bindAddress ; cci est utile sur une machine multi-hte ( plusieurs interfaces rseaux) afin dempcher lacceptation de connexions
dautres adresses locales. Une adresse bindAddress gale null accepte
des connexions nimporte quelle adresse locale.
Mthodes
Socket accept() throws IOException Cette mthode bloque (attend)

VI.3 Sockets serveurs

127

jusqu ce quun client se connecte sur le port sur lequel lobjet de type
ServerSocket est en coute. Un objet s de type Socket est renvoy,
correspondant une connexion TCP avec le client. Sil y a un gestionnaire de scurit, la mthode checkAccept() de ce dernier est appele
avec s.getInetAddress().getHostAddress() et s.getPort() comme arguments afin de sassurer que lopration est permise. Si elle ne lest pas,
une exception SecurityException est leve.
void close() throws IOException Cette mthode ferme le point de communication de type ServerSocket. Il ne ferme aucune des connexions dj
acceptes et non encore fermes, de sorte quun serveur peut fermer ce
point de communication et maintenir les connexions existantes avec ses
clients. Par contre, cet appel informe le systme dexploitation darrter
daccepter de nouvelles connexions de clients.
InetAddress getInetAddress() Renvoie ladresse locale laquelle le
point de communication de type ServerSocket est attach. Si aucune
adresse locale na t spcifie au constructeur, la valeur renvoye correspondra une adresse locale quelconque (typiquement 0.0.0.0).
int getLocalPort() Renvoie le numro de port sur lequel lobjet de
type ServerSocket est en coute. Ceci est utile si le numro 0 a t
fourni et donc un numro de port inutilis a t attribu par le systme
dexploitation.
void setSocketTimeout(int timeout) throws SocketException Cette mthode fixe une valeur de temps dexpiration, en milisecondes, aprs laquelle un appel accept() nayant pas reu de rponse sera interrompu
avec une exception de type InterruptedIOException. Une valeur de 0
inhibe le compteur dexpiration et un appel accept() bloque indfiniment.
int getSocketTimeout() throws IOException Renvoie la valeur du temps
dexpiration, ou 0 si aucune expiration nest valide.
protected final void implAccept(Socket s) throws IOException Les
sous-classes de ServerSocket utilisent cette mthode afin de redfinir
accept() et renvoyer leur propre sous classe de Socket. Par exemple un
objet de type MaServerSocket effectuera typiquement un appel du type :
MaSocket ms;
try {
implAccept(ms);
catch (IOException e) { ... }

o MaSocket sera une sous-classe de Socket. Au retour de implAccept(),


lobjet de type MaSocket sera connect un client.

128

Support de cours Java

VI.4

Serveurs itratifs

4.1 Exemple de serveur echo


Lexemple qui suit est un serveur echo itratif (single-threaded). Il accepte
une connexion la fois, sert le client et une fois que le client a termin,
accepte une autre connexion (do le terme itratif). Le service ralis est
le renvoi exact de ce quil a reu.
import java.io.*;
import java.net.*;
public class SingleThreadEchoServer {
public final static int DEFAULT_PORT = 6789;
public static void main (String[] args) throws IOException {
int
port;
// no de port du service
String
line = null;
ServerSocket
server;
Socket
client;
DataInputStream in;
PrintStream
out;
// Tests darguments
switch(args.length) {
case 0 : port = DEFAULT_PORT;
break;
case 1 : try {
port = Integer.parseInt (args[0]);
} catch (NumberFormatException e) {
port = DEFAULT_PORT;
}
break;
default :
throw new IllegalArgumentException ("Syntaxe : STServer [<port>]");
}
// Creation de socket serveur
System.out.println ("Demarrage sur le port " + port);
server = new ServerSocket (port);
System.out.println ("En ecoute ...");
// Boucle generale et service

VI.4 Serveurs itratifs

129

while (true) {
// Acceptation de connexion
client = server.accept();
System.out.println ("Connexion a " + client.getInetAddress() +
" acceptee");
System.out.flush();
// Capture des flux de reception et denvoi
in = new DataInputStream(client.getInputStream());
out = new PrintStream(client.getOutputStream());
//----------------------------// TRAITEMENT DU SERVICE DECHO
//----------------------------while (true) {
// lire une ligne
line = in.readLine();
if ((line == null) || (line.equals("exit")))
break;
// ecrire la ligne
out.println(line);
}
if (line == null) {
// Sortie du service : le client a termine
System.out.println ("Fermeture de socket client");
client.close ();
} else if (line.equals("exit")) { // Fermeture de socket serveur
System.out.println ("Fermeture des socket client & serveur");
client.close ();
server.close ();
break;
}
}// while(true)
}/* main() */
}/* class STServer */

130

Support de cours Java

VI.5

Serveurs non bloquants

5.1 Exemple de serveur echo


Lexemple qui suit est un serveur echo itratif (single-threaded) non bloquant, en se servant de la mthode InputStream.available() (qui renvoie
le nombre doctets qui peuvent tre lus lors dune prochaine lecture).
Plusieurs problmes se posent avec cette approche. Le plus gnant est que,
faisant n accept() pour recevoir n clients, on doit attendre que les n se
soient effectivement connects . . .
import java.io.*;
import java.net.*;
public class NBServer {
static InputStream in0, in1;
static OutputStream out0, out1;
protected int
port = 0;
public static void main (String[] args) throws IOException {
// Tests darguments
if (args.length != 1)
throw new IllegalArgumentException ("Syntaxe : NBServer <port>");
port = Integer.parseInt(args[0]);
System.out.println ("Initialisation sur le port " + port);
ServerSocket server = new ServerSocket (port);
while(true)
try {
accept (Integer.parseInt (args[0]));
int x0, x1;
while (((x0 = readNB (in0)) != -1) &&
((x1 = readNB (in1)) != -1)) {
if (x0 >= 0)
out1.write (x0);
if (x1 >= 0)
out0.write (x1);
}
} finally {
System.out.println ("Closing");
close (out0);
close (out1);

VI.5 Serveurs non bloquants

131

}
}// main()

// fermeture de socket
static void close (OutputStream out) {
if (out != null) {
try {
out.close ();
} catch (IOException ignored) { ; }
}
}// close()

static void accept (int port) throws IOException {


try {
System.out.println ("En ecoute de client 1 ...");
Socket client0 = server.accept ();
System.out.println ("Client 1 connecte de " +
client0.getInetAddress ());
in0 = client0.getInputStream ();
out0 = client0.getOutputStream ();
out0.write ("Veuillez patienter ...\r\n".getBytes ("latin1"));
System.out.println ("En ecoute de client 2 ...");
Socket client1 = server.accept ();
System.out.println ("Client 2 connecte de " +
client1.getInetAddress ());
in1 = client1.getInputStream ();
out1 = client1.getOutputStream ();
out1.write ("Bonjour.\r\n".getBytes ("latin1"));
out0.write ("Veuillez continuer.\r\n".getBytes ("latin1"));
} finally {
server.close ();
}
}
// Fonction de lecture non bloquante
static int readNB (InputStream in) throws IOException {
if (in.available () > 0)
return in.read ();
else
return -2;
}

132

Support de cours Java

}// class NBServer

VI.6

Serveurs concurrents

6.1 Exemple de serveur echo multi-activits


Ce serveur lit une ligne de texte envoye par le client et la lui renvoie sans
changement.
Le serveur cre un objet ServerSocket qui surveille le port de connexion.
Ds quune connexion avec un client est accepte, lobjet ServerSocket alloue un nouvel objet Socket (connect un nouveau port) pour se charger
des communications ultrieures avec ce client. Le serveur peut reprendre sa
surveillance avec le mme objet ServerSocket en attente dautres connexions
clientes.
Ce serveur est multi-activits (multithreaded). La classe EchoServeur est
implante par une activit qui a dans sa mthode run() une boucle infinie. Cette boucle attend la connexion de nouveaux clients grce lobjet
ServerSocket.
Pour chaque client, elle cre une nouvelle activit (ici un objet Connexion
qui se charge des communications avec le client. Ces communications se font
partir de lobjet Socket renvoy par lobjet ServerSocket.
Le constructeur de la classe Connexion initialise des flux de communication
partir de lobjet Socket et active lexcution de lactivit.
La mthode run() de Connexion gre les communications avec le client ;
cest elle qui fournit effectivement le service, ici un pur et simple renvoi des
lignes reues.
import java.io.*;
import java.net.*;
public class EchoServer extends Thread {
public final static int DEFAULT_PORT = 6789;
protected int port;
protected ServerSocket listen_socket;
// Sortir avec un message derreur lorsquune exception survient
public static void fail(Exception e, String msg) {
System.err.println(msg + ": " + e);
System.exit(1);
}

VI.6 Serveurs concurrents

133

// Creer un ServerSocket pour y ecouter les demandes de connexions;


//
debuter lexecution de lactivite
public EchoServer(int port) {
if (port == 0) port = DEFAULT_PORT;
this.port = port;
try { listen_socket = new ServerSocket(port); }
catch (IOException e) { fail(e,
"Exception en creation de socket serveur"); }
System.out.println("Serveur : en ecoute sur le port no " + port);
this.start();
}

// Corps de lactivite serveur. Effectue une boucle infinie, ecoutant


// et acceptant les connexions de clients. Pour chaque connexion,
// creer un objet Connection pour gerer les communications via la
// nouvelle socket.
public void run() {
try {
while(true) {
Socket client_socket = listen_socket.accept();
Connection c = new Connection(client_socket);
}
}
catch (IOException e) {
fail(e, "Exception en ecoute de connexions");
}
}
// Lancer le serveur, ecoutant sur un port eventuellement specifie
public static void main(String[] args) {
int port = 0;
if (args.length == 1) {
try { port = Integer.parseInt(args[0]); }
catch (NumberFormatException e) { port = 0; }
}
new EchoServer(port);
}
}
// Activite gerant toutes les communications avec un client
class Connection extends Thread {
protected Socket client;

134

Support de cours Java


protected DataInputStream in;
protected PrintStream out;
// Initialiser les flux et debuter lactivite
public Connection(Socket client_socket) {
client = client_socket;
try {
in = new DataInputStream(client.getInputStream());
out = new PrintStream(client.getOutputStream());
}
catch (IOException e) {
try { client.close(); } catch (IOException e2) { ; }
System.err.println("Exception en obtention de flux de sockets : "
+ e);
return;
}
this.start();
}
// Fournit effectivement le service
// Lire un ligne et la renvoyer inchangee
public void run() {
String line;
int len;
try {
for(;;) {
// lire une ligne
line = in.readLine();
if (line == null) break;
// ecrire la ligne
out.println(line);
}
}
catch (IOException e) { ; }
finally { try {client.close();} catch (IOException e2) {;} }
}

VII Invotaion de mthodes distantes :


RMI
Rfrences bibliographiques
Java RMI, W. Grosso [Gro02].

VII.1

Notion dinvocation de mthode distante

1.1 RMI
Historiquement les rseaux se rencontrent dans 2 types dapplications :
le transfert de fichiers, au travers de FTP, SMTP (Simple Mail Transfer
Protocol), HTTP (HyperText Transport Protocol), . . .
xcution dun programme sur une machine distante, au travers de telnet,
rlogin, RPC, . . .
RMI (Remote Method Invocation) : mcanisme permettant un programme
Java dappeler certaines mthodes sur un serveur distant.
Peut tre vu comme un moyen de dfinir des protocoles sur mesure et des
serveurs avec un minimum defforts.

1.2 Transparence du mcanisme


Chaque objet distant implante une interface distante (remote interface) qui
spcifie quelles mthodes peuvent tre invoques (appeles) par les
clients.
Les clients peuvent appeler des mthodes des objets distants presque exactement comme ils appellent des mthodes locales.
Les serveurs Web compatibles Java peuvent implanter des mthodes distantes qui permettent au clients dobtenir un index complet des fichiers du
site.
135

136

Support de cours Java


Ceci peut rduire normment le temps que prend un serveur remplir les
requtes de moteurs de recherche (Web spiders) comme Lycos et Altavista.
Excite utilise dja une version non Java de cette ide.
Du point de vue du programmeur, les objets et les mthodes distants fonctionnent comme les locaux. Tous les dtails dimplantation sont cachs.
Les seules choses raliser sont :
importer un paquetage,
aller chercher lobjet dans un registre (une ligne de code),
grer les RemoteException lorsque lon appelle les mthodes.
Un objet distant est un objet dont les mthodes peuvent tre appeles par
une machine virtuelle Java diffrence de celle o lobjet vit, gnralement
celle dun autre ordinateur.

VII.2

Scurit Srialisation

2.1 Scurit
Les activits dun objet distant sont limites dune manire analogue la
limitation des activits dune applet.
Un objet SecurityManager vrifie quune opration est autorise par le
serveur.
Des gestionnaires de scurit spcifiques peuvent tre dfinis.
Une authentification par cl publique peut tre utilise pour vrifier lidentit dun utilisateur et autoriser diffrents utilisateurs diffrents niveaux
daccs un objet distant.
Par exemple, le grand public peut tre autoris consulter une base de
donnes, mais pas la modifier. Le personnel interne dune entreprise peut
tre autoris consulter et mettre jour la base.

2.2 Srialisation
Lorsquun objet est pass ou renvoy dune mthode Java, ce qui est rllement transfr est une rfrence lobjet.
Dans les implantations actuelles de Java, les rfrences sont des pointeurs
double indirection ; ils pointent sur les zones mmoire de stockage des objets
de la machine virtuelle.
Passer des objets dune machine virtuelle une autre pose quelques problmes.
2 solutions possibles :

VII.2 Scurit Srialisation

137

on passe une rfrence distante (qui pointe sur des zones mmoires de la
machine distante),
on passe une copie de lobjet.
Lorsque la machine locale passe un objet distant la machine distante, elle
passe en fait une rfrence distante. Lobjet na en ralit pas quitt la
machine distante.
Lorsque la machine locale passe lun de ses propres objets la machine
distante, elle ralise une copie de lobjet quelle envoie.
Pour copier un objet, il faut un moyen de convertir cet objet en un flux
doctets.
La srialisation est un schma qui convertit les objets en un flux doctets qui
peut tre envoy dautres machines. Ces dernires reconstruisent lobjet
original partir des octets.
Ces octets peuvent galement tre crits sur disque et lus ultrieurement ;
on peut donc sauvegarder ltat dun objet individuel.
Pour des raisons de scurit, seulement certains objets peuvent tre
srialiss.
Tous les types primitifs ainsi que les objets distants peuvent tre srialiss, mais les objets locaux ne peuvent ltre que sils implantent linterface
Serializable.
Liste des classes srialisables :
Srialisable
java.lang.Character
java.lang.Boolean
java.lang.String
java.lang.Throwable
java.lang.Number
java.lang.StringBuffer
java.util.Hashtable
java.util.Random
java.util.Vector
java.util.Date
java.util.BitSet
java.io.File
java.net.InetAdress
java.net.URL
java.awt.BorderLayout
java.awt.Color
java.awt.Dimension
java.awt.Event
java.awt.Font

Non srialisable
Thread
InputStream et classes filles
OutputStream et classes filles
JDBC ResultSet
...

138

Support de cours Java


java.awt.Polygon
java.awt.CardLayout
java.awt.FontMetrics
java.awt.Image
java.awt.Window
java.awt.FlowLayout
java.awt.GridLayout
java.awt.Point
java.awt.Rectangle
java.awt.MenuComponent
java.awt.Insets
java.awt.CheckboxGroup
java.awt.MediaTracker
java.awt.GridBagLayout
java.awt.GridBagConstraints
java.awt.Cursor
java.rmi.server.RemoteObject

VII.3

Brve description du fonctionnement des


RMI

3.1 Passage darguments


3 mcanismes diffrents sont utiliss pour passer des arguments et en rcuprer dune mthode distante, selon le type de donne passe :
Les types primitifs ( int, double, boolean, . . . ) sont passs par valeur,
de la mme manire quen appel de mthode locale Java.
Les rfrences des objets distants (c..d. des objets implantant linterface Remote) sont passs comme rfrences distantes, qui permettent
au rcepteur dappeler les mthodes de lobjet. Ceci est similaire la
manire dont les rfrences dobjets locaux sont passs des mthodes
locales Java.
Les objets qui nimplantent pas linterface Remote sont passs par
valeur. Cest--dire que des copies compltes sont passes, en utilisant la
srialisation.
Des objets qui ne sont pas srialisables ne peuvent pas tre passs
des mthodes distantes.

VII.3 Brve description du fonctionnement des RMI

139

Des mthodes dobjets distants sexcutent sur le serveur mais peuvent tre
appeles par des objets au sein du client. Des mthodes dobjets non distants et srialisables sexcutent sur le client.

3.2 Communications
Les communications se ralisent par une srie de couches comme suit

Programme serveur

Programme client

Squelette

Souche

Couche de rfrence distante


Couche transport (TCP,
UDP)

Couche de rfrence distante


Couche transport (TCP,
UDP)

= Internet =
En fait le programme client converse avec une souche (stubs) et le programme serveur converse avec un squelette (skeleton).
Un client trouve un serveur appropri en utilisant un registre, qui possde
une mthode lookup().
Lorsquon appelle lookup(), cette dernire trouve la souche ncessaire pour
lobjet dsir, et le charge. Ce squelette peut provenir de nimporte quelle
plate-forme.
Un client appelle une mthode distante en utilisant une souche.
Lsouche (stub) est un objet spcial qui implante les interfaces distantes de
lobjet distant.
la souche possde des mthodes ayant les mmes signatures que
toutes les mthodes que lobjet distant exporte.
De fait, le client pense quil appelle une mthode de lobjet distant, mais en
ralit il appelle une mthode quivalente de la souche.
Les souches sont utiliss dans la machine virtuelle du client la place des
objets rels et de leurs mthodes qui rsident au sein du serveur. Lorsque
la souche est appele, elle passe lappel la couche de rfrence distante
(Remote Reference Layer).
La couche de rfrence distante contient un protocole de rfrence distante
indpendant des souche client et squelette serveur. Cette couche de rfrence distante est responsable de linterprtation des rfrences distantes.
Plus prcisment, les rfrences locales de la souche client sont converties en
rfrences distantes lobjet sur le serveur. Puis, les donnes sont passes
la couche transport.

140

Support de cours Java

3.3 Registres
Dans un mcanisme dappel de procdures distantes (RPC) tel quon le
trouve sous Unix, il y a un gestionnaire de numros de ports (port mapper)
qui maintient une table de paires (nom de service distant, numro de port).
Un processus client voulant effectuer un appel de procdure distant sadresse
au gestionnaire de ports, qui lui renvoie le numro de port correspondant au
service ( la procdure distante) demand. Le processus client peut alors
sy connecter et la procdure distante seffectue.
Le client na besoin de conntre que le numro de port du gestionnaire de
ports et le nom du service (de la procdure distante).
Le mcanisme de RMI, de manire similaire aux RPC, utilise un service
analogue au gestionnaire de port, nomm un (gestionnaire de) registre (registry).
Un registre est gnralement lanc sur la mme machine que le serveur.
un gestionnaire de regsitre correspond une unique paire numro
de de machine, numro de port et rciproquement.
Chaque gestionnaire de registre implante linterface Registry et fournit
donc des services :
denregistrement dobjet (dont des mthodes seront disponibles de manire distante),
dobtention de rfremce distante un objet,
de listage des objets enregistrs,
dannulation denregistrement dun objet.

VII.4

Implantation

4.1 Aperu des paquetages


4 paquetages pour les rmi :
java.rmi,
java.rmi.server,
java.rmi.registry,
java.rmi.dgc
java.rmi : classes, interfaces et exceptions cot client.
java.rmi.server : classes, interfaces et exceptions cot serveur. Classes
utilises lorsquon crit des objets distants utiliss par des clients.
java.rmi.registry : classes, interfaces et exceptions utilises pour localiser
et nommer des objets distants.
java.rmi.dgc : gestion du ramasse-miettes distribu (dgc, ou Distributed
Grabage Collector).

VII.4 Implantation

141

Dans la suite, et conformment la documentation Sun, on utilisera


ladjectif distant en se rfrant implicitement un serveur et ladjectif local
en se rfrant un client.

4.2 Implantation du serveur


Pour crer un nouvel objet distant, on dfinit une interface qui tend
java.rmi.Remote.
Linterface Remote na aucune mthode. Elle agit comme un marqueur dobjets distants.
Linterface enfant de Remote que lon dfinit dtermine quelles mthodes de
lobjet distant crer peuvent tre appeles par les clients.
Seules les mthodes publiques dclares dans une interface distante
peuvent tre appeles de manire distante.
Chaque mthode de linterface enfant de Remote dfinie par le programmeur doit dclarer lever lexception java.rmi.RemoteException.
Exemple dune interface pour un Hello World distant. Cette interface a
une seule mthode, parler()
import java.rmi.*;
public interface DoueDeParole extends Remote {
public String parler() throws java.rmi.RemoteException;
}

Aprs avoir dfini une interface distante, il faut dfinir une classe qui
implante cette interface et tend java.rmi.UnicastRemoteObject.
La classe UnicastRemoteObject fournit un certain nombre de mthodes qui
se chargent des dtails de la communication distante.
En particulier, il y a le dcoupage et le rassemblement.
Dcoupage (marshaling) : conservion des arguemnts et des valeurs de retour
en un flux doctets qui peuvent tre envoys travers le rseau.
Exemple de BashoServeur, une classe qui implante linterface distante DoueDeParole
et qui tend UnicastRemoteObject
import java.rmi.*;
import java.rmi.server.*;
import java.net.*;
public class BashoServeur extends UnicastRemoteObject
implements DoueDeParole {
public BashoServeur() throws RemoteException {
super();

142

Support de cours Java


}
public String parle() throws RemoteException {
return "La cloche du temple sest tue
" + "\n"
"Dans le soir, le parfum des fleurs " + "\n"
"En prolonge le tintement.
" + "\n"
"Matsuo Basho (1644-1694)" ;
}
public static void main(String args[]) {
try {
BashoServeur b = new BashoServeur();
Naming.rebind("MatsuoBasho", b);
System.out.println("Serveur BahoServeur pret.");
} catch (RemoteException re) {
System.out.println("Exception ds BashoServeur.main() : " + re);
} catch (MalformedURLException re) {
System.out.println("MalformedURLException ds BashoServeur.main() : "
+ re);
}
}// main()
}

Le constructeur BashoServeur() appelle le constructeur par dfaut. Pour


une classe Java locale, ceci na pas besoin detre crit. Ici, on doit dclarer
que toute mthode lve RemoteException.
Notez bien quici le code de la mthode distante quoiDire() nest
pas diffrent de celui qui serait crit pour une application entirement
locale.
Ceci est un gros avantage des RMI : les aspects distants sont pour la
plupart transparents pour le programmeur.
Notez que les mthodes main() et BashoServeur() ne seront pas disponibles
de manire distante. Seule quoiDire() lest (parce que figurant dans une
interface Remote).
Le constructeur est trivial mais doit figurer, afin de dclarer quil lve une
RemoteException.
Il y a enregistrement de lobjet BashoServeur via un Naming.rebind().
Cette mthode place les objets dans un registre et leur associe un nom
fourni en paramtre.
Un client peut ensuite requrir cet objet par son nom ou obtenir une liste
dobjets disponibles.

VII.4 Implantation

143

4.3 Gnration des souche et squelette


On gnre la souche (stub) et le squelette (skeleton) via rmic, que lon
applique aux .class Par ex.
/home/mounier > rmic BashoServeur
/home/mounier > ls Doue* BashoServeur*
BashoServeur.class
BashoServeur_skel.class
BashoServeur.java
BashoServeur_stub.class

DoueDeParole.class
DoueDeParole.java

Arguments : rmic BashoServeur -d -classpath JavaProgs/Tests Loption


-d va mettre le .class dans le rpertoire o le source est trouv (par dfaut,
le .class est plac dans le rpertoire courant).

4.4 Lancement du serveur


Pour lancer le serveur :
On lance dabord le serveur de registre, avec lequel le serveur dialogue :
rmiregistry & (ou start rmregistry sous DOS). Il coute sur le port
1099 par dfaut. Pour quil coute sur un autre port : rmiregistry
2048 &

On lance ensuite le serveur,


/home/mounier > java BashoServeur &
Serveur BashoServeur pret.

4.5 Implantation du client


Pour quun client appelle une mthode distante, il doit rcuprer une rfrence un objet distant.
Pour cela, il rcupre un objet distant en demandant au serveur de rechercher dans un registre. La demande seffectue au travers de la mthode
lookup(String name) quappelle le client.
Le schma de nommage dpend du registre utilis. La classe Naming du
paquetage java.rmi fournit un schma fond sur des URL pour localiser
des objets.
Lobtention de rfrence distante se fera alors par :
Object o1 = Naming.lookup("rmi://sunsite.unc.edu/MatsuoBasho");
Object o2 = Naming.lookup("rmi://sunsite.unc.edu:2048/MatsuoBasho");

Le champ de protocole est ici rmi, qui signifie que lURL rfrence un objet
distant.
Le champ fichier (ici MatsuoBasho) spcifie le nom de lobjet distant.
Les champs de nom de machine et de numro de port optionnel sont inchangs par rapport au cas dHTTP.

144

Support de cours Java


Puisque la mthode lookup() renvoie un Object, il faut effectuer une
conversion de type (cast) en le type dinterface distante que lobjet distant
implante (et non la classe elle-mme, cache des clients) : DoueDeParole b
= (DoueDeParole) Naming.lookup("MatsuoBasho") ;
Exemple de client associ BashoServeur :
import java.rmi.*;
public class BashoClient {
public static void main(String args[]) {
System.setSecurityManager(new RMISecurityManager());
try {
DoueDeParole b = (DoueDeParole) Naming.lookup("MatsuoBasho");
String message = b.parle();
System.out.println("BashoClient : " + message);
} catch (Exception e) {
System.out.println("Exception dans BashoClient.main() : "
+ e);
}
}
}

VII.5

Paquetage java.rmi

5.1 Linterface Remote


Le paquetage java.rmi contient tout ce dont a besoin un client. Il contient
1 interface, 2 classes et une poigne dexceptions.
Linterface Remote ne dclare aucune mthode. Elle sert de marqueur pour
les objets distants.
On implante une interface sous classe de Remote.

5.2 La classe Naming


java.rmi.Naming est un gestionnaire de registre qui associe un URL de
la forme rmi ://machine.domaine.surdomaine/monObjetDistant un objet
distant.
Chaque entre du registre a un nom et une rfrence dobjet. Les clients
donnent le nom, et rcuprent une rfrence un objet distant.
Naming est le seul type de registre actuellement disponible avec les RMI.
Son plus gros dfaut est que le client doit connatre le serveur o rside
lobjet distant. En outre, le schma de nommage nest pas hirarchis.

VII.5 Paquetage java.rmi

145

La classe Naming a 5 mthodes :


methode()

But

public static Remote lookup(String


url) throws RemoteException,
NotBoundException, AccessException,
UnknownHostException

Renvoie une souche (stub) associ lobjet distant rfrenc par lURL url. Cette
souche agit pour le client comme lobjet distant.
RemoteException est leve si lookup()
narrive pas trouver le registre spcifi
(souvent la suite dun connection time
out).
NotBoundException est leve si le nom spcifi dans url nest pas rpertori dans le
registre utilis.
AccessException est leve si le client nest
pas autoris se connecter ce registre.
UnknownHostException est leve si la machine rfrence dans url ne peut tre localise.
Un serveur utilise bind() pour associer un
nom comme MatsuoBasho un objet distant.
Si url est dja associe un objet local,
bind() lve une AlreadyBoundException.
Enlve lobjet scifi par url du registre
(effectue loppos de bind()).

public static void bind(String url,


Remote ro) throws RemoteException,
AlreadyBoundException,
AccessException,
UnknownHostException
public static void
unbind(String url, Remote
ro) throws RemoteException,
NotBoundException, AccessException,
UnknownHostException
public static void rebind(String
url, Remote ro) throws
RemoteException, AccessException,
UnknownHostException
public static String[]
list(String url) throws
RemoteException, AccessException,
UnknownHostException

Travail similaire celui de bind(), sauf


que lassociation seffectue mme si url
tait dja associe un objet distant. Dans
ce cas, lancienne association est perdue.
Renvoie tous les URL qui sont associs,
sous forme de tableau de chanes. On doit
fournir le nom du registre la fin de url.

5.3 La classe RMISecurityManager


Les souches sont en un sens comme des Applets, du point de vue de la
scurit. Ils transportent du code dune autre machine qui vient sexcuter

146

Support de cours Java


localement.
La machine virtuelle Java ne laisse sexcuter un tel code que sil y a un
gestionnaire de scurit en place.
Celui par dfaut avec les RMI RMISecurityManager, ne permet quun accs
minimum des objets distants.
Un seul constructeur, sans argument. Utilisation usuelle :
System.setSecurityManager(new RMISecurityManager()) ;
La mthode getSecurityContext() dtermine lenvironnement pour lac-

cs certaines oprations. Plus doprations sont permises par une classe


charge localement quune charge de manire distante.
lavenir, une authentification par signature cl publique permettra de
donner diffrents utilisateurs diffrents niveaux daccs.
Il y a 23 mthodes destines tester diverses oprations afin de voir si elles
sont permises. Chacune lve une StubSecurityException si lopration est
interdite. Sinon, la mthode se termine et ne renvoie rien.
Liste des mthodes scuritaires :
methode()

But

Une souche peut-elle crer un


ClassLoader ?
checkAccess(Thread t)
Un souche peut-elle manipucheckAccess(ThreadGroup
ler des activits en dehors de
g)
son propre groupe dactivits ?
checkExit(int status)
Un souche peut-elle appeler
System.exit() ?
checkExec(String cmd)
Une souche peut-elle appeler
System.exec() ?
checkLink(String lib)
Une souche peut-elle tre li
une bibliothque dynamique ?
checkPropertiesAccess()
Une souche peut-elle lire les
proprits (variables denvironnement) de la machine locale ?
checkPropertyAccess(String Une souche peut-elle verifier
key)
une proprit doonne
checkRead(String file)
Une souche peut-elle lire un fichier ?
checkRead(String file,
URL base)
checkRead(String file,
Object context)
checkRead(FileDescriptor
fd)
checkWrite(String file)
Une souche peut-elle crire
sur un fichier ?
checkCreateClassLoader()

Souche locale
Non

Souche
tante
Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

Non

dis-

VII.5 Paquetage java.rmi


checkWrite(FileDescriptor
fd)
checkListen(int port)

checkAccept(String host,
int port)
checkConnect(String
host, int port)

checkConnect(String
host, int port, Object
context)
checkConnect(String
fromHost, String toHost)
checkTopLevelWindow(Object
Window)
checkPackageAccess(String
pkg)

147

Une souche peut-elle couter


les demandes de connexion
sur un port ?
Une souche peut-elle accepter
les demandes de connexions
sur un port ?
Une souche peut-elle ouvrir
une connexion host sur
port ?

La Souche peut-elle crer une


nouvelle fentre ?
Une souche peut-elle crer le
paquetage pkg ?

checkPackageDefinition(String
Une souche peut-elle crer des
pkg)
classes au sein du paquetage
pkg ?

checkSetFactory()

Une souche peut-elle monter


une usine distante (network
factory) ?

Non

Non

Non

Non

Non

Non

Non

Non

Oui,
si
la
machine
est
lune partir
desquelles
la souche a
t charge ;
sinon, non.
Non

Non

Non

Non

Non

Non (pourra
tre ajustable
par lutilisateur dans le
futur)
Non (pourra
tre ajustable
par lutilisateur dans le
futur)
Non

Non (pourra
tre ajustable
par lutilisateur dans le
futur)
Non (pourra
tre ajustable
par lutilisateur dans le
futur)
Non

Il y a galement 17 exceptions qui sont dfinies dans java.rmi. Un programme rseau peut avoir beaoucoup de raisons de ne pas fonctionner correctement. Pour avoir des programmes relativement fiables, il est ncessaire de grer (par catch) les diverses exceptions susceptibles dtre leves
par les mthodes utilises.
En 1 ire approximation, on peut se contenter de ne grer que RemoteException,
dont les 16 autres drivent.

148

Support de cours Java

VII.6

Paquetage java.rmi.registry

6.1 Interface Registry


1 interface : Registry, qui spcifie le comportement dun gestionnaire de
registre.
Linterface Registry a un champ de donnes : public final static int
REGISTRY_PORT qui contient le numro de port associ au gestionnaire de
registre. La valeur par dfaut est 1099.
Elle spcifie les mthodes :
bind(),
list(),
lookup(),
rebind(),
unbind()
qui ont t dtailles en voyant la classe Naming.

6.2 Classe LocateRegistry


1 classe : LocateRegistry, qui sert localiser des gestionnaires de registres
(obtenir une rfrence en donnant un n de port) et en crer dautres.
Toutes les mthodes sont static et il ny a pas de constructeur.
Les mthodes sont les suivantes :
methode()

But

public static Registry


createRegistry(int port) throws
RemoteException

Cre un gestionnaire de registre associ au


numro de port port. lutilisateur doit
avoir le droit dassocier un service au numro de port spcifi (sous Unix, il faut
tre root pour pouvoir fournir un numro
de port infrieur 1024). Une exception
RemoteException est leve en cas dchec.
Renvoie une rfrence au gestionnaire de
regsitre situ sur la machine locale et associ au numro de port 1099. Une exception
RemoteException est leve en cas dchec.
Renvoie une rfrence au gestionnaire de
regsitre situ sur la machine locale et associ au numro de port port. Une exception
RemoteException est leve en cas dchec.

public static Registry


getRegistry() throws
RemoteException
public static Registry
getRegistry(int port) throws
RemoteException

VII.7 Paquetage java.rmi.server


public static Registry
getRegistry(String host)
throws RemoteException,
UnkownHostException

public static Registry


getRegistry(String host, int
port) throws RemoteException,
UnkownHostException

VII.7

149
Renvoie une rfrence au gestionnaire de
regsitre situ sur la machine host et
associ au numro de port 1099. Si la
localisation de
host choue, une exception UnkwonHostException est leve.
Toute autre cause dchec gnre une
RemoteException.
Renvoie une rfrence au gestionnaire de
regsitre situ sur la machine host et
associ au numro de port port. Si
la localisation de host choue, une exception UnkwonHostException est leve.
Toute autre cause dchec gnre une
RemoteException.

Paquetage java.rmi.server

7.1 Rapide description gnrale


Ce paquetage contient 6 exceptions, 7 interfaces et 10 classes. Il ne doit tre
en gnral quutilis par les serveurs.
Les classes importantes sont :
RemoteObject, la classe de base pour tous les objets distants,
RemoteServer, classe abstraite qui tend RemoteObject et spcifie un
serveur RMI.
UnicastRemoteObject, qui implante concrtement RemoteServer.

7.2 Classe RemoteObject


La classe RemoteObject joue le rle d Object pour des objets distants.
Elle fournit une implantation des mthodes suivantes :
toString(), qui renvoie une description en String dun objet distant.
Elle renvoie dans cette chane le nom de machine, le numro de port du
gestionnaire de registre et un numro de rfrence de lobjet.
hashCode(),
clone() et
equals(), qui compare 2 rfrences distantes et renvoie true si elles
pointent sur le mme objet.

150

Support de cours Java

7.3 Classe RemoteServer


Cette classe tend RemoteObject. Cest une classe abstraite donnant lieu
des implantations concrtes, comme UnicastRemoteObject (la seule implantation disponible lheure actuelle).
Constructeurs : protected RemoteServer() protected RemoteServer(RemoteRef
r) On ne les instanciera pas. On crera des instances de UnicastRemoteObject.
RemoteServer a 2 mthodes pour obtenir des informations sur le client avec
lequel on communique :
public static String getClientHost() throws ServerNotActiveException
qui renvoie une String contenant le nom de la machine du client qui a
invoqu la mthode contenant lappel getClientHost() ;
public int getClientPort() throws ServerNotActiveException qui renvoie une String contenant le numro de port du client qui a invoqu la
mthode contenant lappel getClientPort(). Rappelons que les numros
de ports client permettent au serveur didentifier les diffrents clients.
Chacune de ces mthodes peut lever une ServerNotActiveException si lactivit (thread) courante nexcute pas une mthode distante.
Pour le debugging, il est parfois utile de voir les appels effectus des objets
distants ainsi que leurs rponses. On peut enregistrer ces informations dans
un fichier log via : public static void setLog(OutputStream os). Si os
est gal null, lenregistrement est stopp.
On peut obtenir une rfrence au flux denregistrement par public static
void getLog(OutputStream os). Ainsi par exemple, on peut ajouter ses
propres informations :
PrintStream p = RemoteServer.getLog();
p.println("Il y a eu " + n + " appels a lobjet distant");

7.4 Classe UnicastRemoteObject


On dfinira en gnral une sous classe de UnicastRemoteObject.
2 mthodes :
public Object clone() throws CloneNotSupportedException qui cre
un clone de lobjet distant appelant.
public static RemoteStub exportObject() throws RemoteException On
utilise cette mthode pour exporter des objets qui ne sont pas des sous
classes de UnicastRemoteObject. Lobjet doit alors implanter linterface
Remote et sexporter lui-mme avec exportObject().

Bibliographie
[Boo94]

G. Booch. Object-Oriented Analysis and Design with Applications.


Benjamin/Cummings, Redwood City, CA, 1994. d. franaise : Analyse & conception orientes objets, AddisonWesley France, Paris,
1994. Un classique de lingnirie du logiciel.

[Bud98]

T. Budd. Understanding Object-Oriented Programming with JAVA.


Addison-Wesley, Reading, MA, 1998. http ://www.awl.com/cseng.
Excellent livre. Explique le pourquoi (vue comprhensive du langage)
de la programmation java et pas seulement le comment (simple vue
descriptive).

[GHJV95] E. Gamma, R. Helm, R. Johnson, et J. Vlissides. Design Patterns.


Elements of Reusable Object-Oriented Software. Addison-Wesley, Reading, MA, 1995. Livre fondateur dune technique maintenant classique
en gnie logiciel.
[GJS96]

J. Gosling, B. Joy, et G. Steele. The Java Language Specification.


Addison-Wesley, Reading, MA, 1996. Description trs dtaille du
langage par leurs fondateurs ; assez aride.

[Gro02]

W. Grosso. Java RMI. OReilly, Beijing, 2002. Un excellent livre de


programmation et conception de serveurs en RMI.

[Har97]

E.R. Harold. Java Network Programming. OReilly, Cambridge, 1997.


Un fort bon livre sur les threads en Java.

[Lea97]

D. Lea. Concurrent Programming in Java. Design Principles and Patterns. Addison-Wesley, Reading, MA, 1997. Un des meilleurs, sinon
le meilleur, livre de programmation concurrente en Java.

[OW97]

S. Oaks et H. Wong. Java Threads. OReilly, Cambridge, 1997. Un


trs bon livre sur les threads en Java.

[RDBF02] G. Roussel, E. Duris, N. Bedon, et R. Forax. Java et Internet


Concepts et programmation. Vuibert, Paris, 2002. Tome 1 Ct
151

152

Support de cours Java


client. Un excellent livre de programmation rseau. Trs bonne introduction diverse aspects du langage hors rseau.

[Ven99]

B. Venners. Design techniques. articles about java program design,


19981999. Divers articles excellents sur le design en Java. lire
absolument.

[Wei98]

M. A. Weiss.
Using Java.

Data Stuctures & Problem Solving


Addison-Wesley,
Reading,
MA,
1998.
http ://www.awl.com/cseng/titles/0-201-54991-3. Bon livre sur
un sujet ultra-classique.

Вам также может понравиться