Académique Documents
Professionnel Documents
Culture Documents
Java
Pascale LE CERTEN
lecerten@irisa.fr
Lucien UNGARO
ungaro@irisa.fr
mars 1999
Classes et objets
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
2
10
11
3.2
3.3
3.4
3.5
3.6
3.7
4
1
2
4
4
5
5
5
6
6
8
Introduction
Dfinition dune classe
Dclaration et cration dobjets
Utilisation des objets
Rdaction des mthodes
Dclaration de mthode
Instance courante : this
Composants non lis des objets : static
Abstraction de la reprsentation : private et public
Classes internes
Types en Java
Types primitifs
Types classes et tableaux
Les rfrences
Chanes de caractres : String et StringBuffer
Donnes constantes - final
Instructions
Affectation
Conditionnelle
Boucles
Instruction cas
Entres/sorties
Traitement dexception : throw-try-catch
12
12
13
13
15
17
19
19
19
20
20
21
22
Hritage
4.1
4.2
4.3
4.4
Interfaces - polymorphisme
26
27
27
30
30
31
32
7.1
7.2
7.3
7.4
8
8.3
8.4
10
Cration de processus
Exclusion et moniteurs : synchronized
Exclusion
Moniteurs
Attente explicite : wait - notify
Attente quune condition soit satisfaite : wait()
Rveil des processus : notify() et notifyAll()
Utilisation - Analogie avec les Moniteurs de Hoare
Rflexions sur lusage des processus
Nature des processus
Intrts des processus
70
70
73
77
12
48
49
49
51
52
52
52
54
55
55
56
10.1
10.2
10.3
10.4
11
41
43
44
45
78
81
83
85
86
87
90
92
93
94
95
134
Classes et objets
1.1 Introduction
La programmation par objets consiste essentiellement structurer les applications
selon les types de donnes. Lexprience montre que lon obtient ainsi des logiciels
fiables, volutifs et faciles maintenir. Le programmeur par objets se pose en premier
la question : quels sont les objets fondamentaux de lapplication ? Quelles sont les
choses et les concepts qui constituent le domaine de lapplication ? Il dcrit alors chaque type dobjet sous forme de variables qui caractrisent son tat puis il sintresse
aux oprations que peuvent subir ces objets et les dcrit sous forme de procdures.
Ceci conduit une modularit base sur les types de donnes. Les traitements sont
toujours dfinis propos de la dfinition dun type de donnes, et non pas isolment
comme cest le cas pour une modularit base en priorit sur la dcomposition des
traitements. Par exemple, pour une gestion de rseau ferroviaire, le programmeur par
objets sera tent de dfinir les objets suivants : trains, voitures, lignes, gares, ... Pour
chaque type dobjet, il dfinira les proprits de ces objets et les actions que lon peut
leur faire subir : un train est form dune collection de voitures, on peut lui accrocher
une voiture, lui dcrocher une voiture, chaque voiture a une capacit ...
Considrons un exemple trs lmentaire : un robot (simpliste). Un tel robot occupe
une certaine position (X,Y), il a une orientation parmi {Nord, Est, Sud, Ouest}, il est
initialis avec une position et une orientation donnes, il peut tourner droite, il peut
avancer dun pas ... On le dcrira par une association de variables dtat et de procdures. Cest une telle association de donnes et de procdures que lon nomme habituellement un objet.
objet
=
Nord
Y
Ouest
Est
Sud
donnes
+
X
Y
orientation
Initialisations
avancer
procdures
tournerADroite
Classes et objets
Classes et objets
class Robot {
public
public
public
public
static
static
static
static
final
final
final
final
int
int
int
int
Nord = 1; (constantes)
Est = 2;
Sud = 3;
Ouest = 4;
public int X;
public int Y;
public int orientation;
(variables dtat)
break;
break;
break;
break;
Lattribut public, appliqu un composant quelconque, donne ou mthode, signifie que ce composant est dsignable par son identificateur depuis le texte de toute
autre classe. Lattribut final, lorsquil est appliqu un composant qui est une donne, signifie que ce composant est constant.
Initialisation des objets : constructeur
Une classe peut dfinir une ou plusieurs procdures dinitialisation appeles constructeurs. Ces procdures ont le mme nom que la classe, et sil y en a plusieurs, elles se
distinguent par le type ou le nombre des paramtres :
Robot(int x, int y, int o)
Robot()
Si on ne dfinit pas explicitement de constructeur, Java dfinit un constructeur par
dfaut, qui est sans paramtre, et qui ne fait pas grand chose (pour une classe hritire
il se borne appeler le constructeur sans paramtre de la classe de base : voir plus loin
le chapitre 4, page 26, sur lhritage).
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999
Classes et objets
totor
null
vigor
null
objet Robot
Classes et objets
Classes et objets
Classes et objets
.
26
12
53
12
sommet = 26
empiler 44
44
26
12
53
12
sommet = 44
dpiler
26
12
53
12
sommet = 26
dpiler
12
53
12
sommet = 12
PileEnt()
interface
visible
empiler(int)
dpiler()
int sommet()
cach
Dans la dfinition dune classe, pour rendre inaccessible un membre par son identificateur, on le dclare la suite du mot cl private.
class PileEnt {
private int s;
private int P[] = new int[100];
public PileEnt() {s=-1;}
public void empiler(int e) {s=s+1; P[s]=e;}
public void depiler() {s=s-1;}
public int sommet() {return P[s];}
}
Avec cette dfinition, les utilisateurs de la classe PileEnt, cest--dire les morceaux
de programme qui dclarent, crent ou utilisent des objets de cette classe, nont directement accs quaux mthodes empiler, dpiler, sommet et au constructeur
PileEnt. Seuls les textes qui dfinissent la classe ont le droit dutiliser s et P.
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999
Classes et objets
En Java les classes peuvent tre regroupes en paquetages (voir plus loin). Si on ne
prcise aucun paquetage particulier, les classes sont places dans le paquetage par
dfaut. Sil nest pas qualifi private, un composant est accessible depuis tout le
paquetage auquel appartient sa classe (il est donc par dfaut public dans son paquetage), mais inaccessible depuis les autres paquetages.
Pour rendre un composant accessible depuis tous les paquetages, il faut le dclarer la
suite du mot cl public .
Classes et objets
lise le parcours partir du sommet, suivant() passe au suivant dans la pile, element() rend llment courant du parcours et estEnFin() teste si le parcours est
fini.
class PileEnt {
private int s; private int P[] = new int[100];
public PileEnt() {s=-1;}
public void empiler(int e) {s=s+1; P[s]=e;}
public void depiler() {s=s-1;}
public int sommet() {return P[s];}
public class Parcours {
private int courant;
public Parcours() {courant=s;}
public int element() {return P[courant];}
public void suivant(){courant--;}
public boolean estEnFin(){return courant==-1;}
}
}
Exemple dutilisation :
PileEnt p= new PileEnt(); ...
// deux parcours sur p :
PileEnt.Parcours parc1= p.new Parcours();
PileEnt.Parcours parc2= p.new Parcours();
parc1.element(); parc1.suivant();
parc2.element(); parc2.suivant();
Noter la forme (surprenante) de la cration dun objet instance dune classe interne :
objet.new constructeur
javac FlipFlop.java
FlipFlop.class
TestFlipFlop.class
Compteur.class
javac Compteur.java
java TestFlipFlop
flip
flop
flip
flop
Les commentaires
Les commentaires peuvent tre nots de trois faons :
soit entre /* et */, qui est la forme hrite de C : /* blabla */
soit entre /** et */, commentaires de documentation, transformables par le
logiciel Javadoc pour produire une documentation HTML : /** blabla */
soit entre // et la fin de la ligne : // blabla
rpertoire de travail
class TestPackage {
public static void main(String argv[]) {
p1.A a = new p1.A();
p1.B b = new p1.B();
}
}
TestPackage.class
A.java
package p1;
rpertoire p1
public class A{
public A() {
System.out.println("A");
}
}
A.class
B.java
package p1;
public class B{
public B() {
System.out.println("B");
}
}
B.class
Remarque : un fichier source doit comporter au plus une classe public et doit avoir
le mme nom que la classe public sil y en a une.
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999
11
Les rpertoires o Java doit effectuer les recherches de paquetages sont gnralement
indiqus par une variable denvironnement. Sous UNIX cette variable denvironnement sappelle CLASSPATH, et elle contient les noms de rpertoires sous la forme
suivante :
/usr/local/java/jdk1.1.4/lib/classes.zip:/home/junon/d03/
maitres/dupont/JAVA:.
Dans cet exemple, /usr/local/java/jdk1.1.4/lib est le rpertoire qui contient les paquetages de la bibliothque Java sous forme dun fichier compress classes.zip. Le rpertoire /home/junon/d03/maitres/dupont/JAVA
contient les paquetages de lutilisateur DUPONT. Le rpertoire . signifie le rpertoire
courant.
Bibliothque
Java offre une bibliothque standardise, sous forme de paquetages thmatiques qui
couvrent la plupart des sujets dintrts actuels :
java.lang : classes de base du langage (chanes, math, processus, exceptions,...),
java.util : structures de donnes (vecteurs, piles, tables, parcours,...),
java.io
: entres-sorties classiques (texte sur clavier-cran, fichiers,...),
java.awt : interfaces homme-machine( fentrage, vnements, graphique, ...),
java.net : communications Internet (manipulation dURL, de sockets,...),
java.applet
: insertion de programmes dans des documents HTML
12
13
Un objet de type Robot est cr et sa rfrence est capte par lidentificateur totor.
Aprs laffectation vigor=totor, lidentificateur vigor capte la rfrence associe totor, et donc vigor dsigne le mme objet. Cela se voit lexcution : lorsque lon fait avancer vigor, cela modifie la position de totor, puisque cest le
mme objet.
.
totor
X
Y
orientation
vigor
Tests dgalit
Loprateur de test dgalit (==) existe pour les expressions de type classe ou
tableau, mais il signifie la comparaison des rfrences et non celle des valeurs des
objets. Il permet donc de savoir si deux expressions dsignent le mme objet.
Si on a besoin de tester lgalit des valeurs, il faut la programmer. Par convention on
le fait au moyen dune mthode que lon appelle equals.
class TestEgalite {
public static void main(String argv[]) {
Robot totor = new Robot(20,30,Robot.Nord);
Robot kador = new Robot(20,30,Robot.Nord);
Robot vigor;
vigor = totor;
if (vigor==totor) {System.out.println("vigor==totor");}
else
{System.out.println("vigor!=totor");}
if (kador==totor) {System.out.println("kador==totor");}
else
{System.out.println("kador!=totor");}
if (kador.equals(totor))
{System.out.println("kador equals totor");}
else
{System.out.println("kador non equals totor");}
}
}
Dans lexemple ci-dessus, le test vigor==totor est vrai, car vigor dsigne le
mme objet que totor. En revanche kador==totor est faux. Lgalit des
valeurs des objets rfrencs par kador et totor peut tre teste par une mthode
equals de la classe Robot :
boolean equals(Robot r) {
return X==r.X && Y==r.Y && orientation==r.orientation;
}
Le mme mcanisme sapplique aux tableaux : loprateur == entre tableaux teste si
deux tableaux sont le mme objet.
De faon similaire, loprateur != entre expressions de type classe ou tableau teste si
ces expressions dsignent des objets diffrents.
14
T
On peut toutefois directement crer une matrice, cest--dire un tableau de tableaux de
mme taille par :
M= new int[3][3]; cre une matrice 3 x 3;
15
La classe StringBuffer offre des objets chanes modifiables, cest dire des
objets dont on peut changer des morceaux, dont on peut changer la taille, ...
Le principal usage de cette classe est la construction progressive et efficace de chanes.
Constructeurs :
StringBuffer(); chane vide
StringBuffer(int length); chane de taille length, non initialise
StringBuffer(String s); chane initialise avec la valeur de la String s
Exemples de mthodes :
StringBuffer append(String s);
ajoute les caractres de s en fin de this
StringBuffer append(char c);
ajoute le caractre c en fin de this
String toString();
chane constante ayant pour valeur la valeur courante de this
int length(); longueur de this.
Remarque : append agit sur this, mais de plus il rend this en rsultat. Cest
pourquoi son type est StringBuffer et non pas void.
Lexemple suivant illustre lusage de StringBuffer. La classe Lecture offre la
mthode chaine(String delimiteurs) pour la lecture de chanes dlimites
par un des dlimiteurs donns en paramtre :
16
class Lecture {
...
public static String chaine(String delimiteurs) {
// lecture dune chaine comprise entre delimiteurs
StringBuffer b = new StringBuffer();
char c=unCar(); // lecture dun caractere
// ignore les delimiteurs de tete
while (delimiteurs.indexOf(c)!=-1) {c=unCar();};
// lit jusquau prochain delimiteur
while (delimiteurs.indexOf(c)==-1)
{b.append(c); c=unCar();};
return b.toString();
}
}
class TestStringBuffer {
public static void main(String argv[]) {
String s;
while(!(s=Lecture.chaine(" ,\r\n")).equals("fin")) {
System.out.println(s);
};
}}
17
18
3.5 Instructions
Pour rdiger les mthodes, on dispose, entre autres, des instructions traditionnelles :
affectation de variable, appel de mthode et instructions construites au moyen des
structures de contrle usuelles telles que conditionnelle, boucle, cas.
3.5.1 Affectation
Forme gnrale : variable = expression;
value lexpression et donne sa valeur la variable.
La variable peut tre simplement dsigne par un identificateur :
i=i+1 ; incrmente i
Elle peut galement tre dsigne par une expression plus ou moins complique :
M[2][6]=12; range 12 dans llment 2,6 de la matrice M
Robot.leMeilleur(totor,vigor).X = 12;
affecte le champ X de lobjet Robot rendu en rsultat par la mthode leMeilleur.
En java, comme en C et C++, certaines oprations daffectation dusage frquent,
comme les incrmentations ou les dcrmentations, admettent une notation abrge :
i++
i--
i=i+1
i=i-1
Ces formes sont utilisables en tant quexpressions, et elles ont la valeur de i avant
affectation.
3.5.2 Conditionnelle
if (cond) {instructions}
Excute instructions si cond est vraie.
if (cond) {instructions1} else {instructions2}
Excute instructions1 si cond est vraie, excute instructions2 si cond est fausse.
Une forme plus gnrale est :
if
(cond1)
else if (cond2)
...
else if (condk)
else
{instructions1}
{instructions2}
{instructionsk}
{instructions}
19
3.5.3 Boucles
Boucle tantque
while (cond) {corps}
Excute les instructions de corps tant que cond est vraie.
Boucle pour
for (init; cond; progression) {corps}
Cette forme de boucle est quivalente
init; while (cond) {corps progression;}
Dans une utilisation saine, init est une instruction qui initialise la (ou les) variable qui
contrle litration, cond est la condition de poursuite, et progression est une instruction qui fait voluer la (ou les) variable de contrle.
On peut de plus dclarer une variable dans la partie init, ce qui cre la variable de contrle de boucle juste pour cette itration.
Exemple : calcul de la somme des lments dun tableau T[0]...T[9]
int s=0;
for (int i=0; i<10; i=i+1) {s=s+T[i];};
20
3.6 Entres/sorties
Java offre deux sortes de communications avec lextrieur :
des entres-sorties traditionnelles : lectures et critures de textes sur clavier/
cran ou sur fichiers,
des interactions travers un systme de fentrage : cration de fentres, cration
de botes de dialogues, raction des vnements.
Le systme de fentrage est prsent au chapitre 7, page 41.
Pour les entres-sorties cran/clavier, on dispose des appels de mthodes suivants :
System.out.print(String s);
impression de la chane s,
System.out.println(String s); idem avec retour la ligne.
System.in.read();lecture dun caractre.
Ce sont des mthodes des classes PrintStream et InputStream dfinies dans le
paquetage java.io. Leur profil est :
void println(String s);
void print(String s);
int System.in.read() throws IOException;
System est une classe du paquetage java.lang. Cette classe dfinit lobjet out
de classe PrintStream pour les impressions sur cran et lobjet in de classe
InputStream pour les lectures au clavier.
La mthode read() ncessite quelques explications : elle lit un caractre, mais elle
rend un int qui est le code ASCII du caractre frapp (cest curieux, mais cest
ainsi). Pour obtenir le caractre Java officiel correspondant, de type char, il faut
pratiquer une conversion (un cast) qui se note ainsi :
char c; ... c = (char) System.in.read();
En outre, cette mthode est susceptible de dclencher une exception (voir paragraphe
suivant) dans le cas o la lecture se passe mal (cela ne peut pas se produire pour une
lecture clavier, mais read() est plus gnrale). Le mcanisme des exceptions est
dcrit au paragraphe suivant. Ceci oblige utiliser cette mthode au sein dun bloc
try qui prvoit un ventuel traitement dexception :
try { c = (char) System.in.read(); }
catch(IOException e) {c= '#';};
Dans cet exemple, le traitement dexception affecte le caractre '#' c.
21
Pour programmer les exercices, on utilisera avec profit les mthodes de la classe
Lecture dont le source est donn ci-dessous.
public class Lecture {
public static char unCar() {
// lecture dun caractere
char c;
try { c = (char) System.in.read();
} catch(IOException e) {c= (char) 0;};
return c;
}
public static char unCarCmde() {
// lecture dun caractere
// et consommation jusquau retour chariot
char c;
try { c = (char) System.in.read();
while(System.in.read()!=\n){};
} catch(IOException e) {c= (char) 0;};
return c;
}
public static String chaine(String delimiteurs) {
// lecture dune chaine comprise entre delimiteurs
StringBuffer b = new StringBuffer();
char c=unCar();
// ignore les delimiteurs de tete
while (delimiteurs.indexOf(c)!=-1) {c=unCar();};
// lit jusquau prochain delimiteur
while (delimiteurs.indexOf(c)==-1)
{b.append(c); c=unCar();};
return b.toString();
}
public static int unEntier() {
// lecture dun entier represente en decimal
String s=Lecture.chaine(" ,.\n");
try { return Integer.parseInt(s);
} catch(NumberFormatException e) {return 0;};
}
}
Si une mthode p() utilise une mthode q() susceptible de dclencher une exception, il faut :
soit capter, et ventuellement traiter, lexception au moyen dun bloc try au
23
Exercice 1
On considre la pile dentiers dote de moyens de parcours.
Programmer une nouvelle version en ralisant la pile par chanage de maillons. Dfinir deux initialisations pour la classe de parcours :
initialisation sur le sommet de la pile,
initialisation avec ltat dun parcours pass en paramtre.
Rdiger un programme qui :
cre une pile et y empile 7, 5, 4, 6, 5, 2, 3
utilise les moyens de parcours pour chercher la premire valeur qui figure en
24
La liste de ses enfants est initialise vide et cette personne est rajoute dans la liste
des enfants de son pre et de sa mre.
nom Adam
pre ?
mre ?
enfants
nom Abel
pre .
mre .
enfants
nom Eve
pre ?
mre ?
enfants
nom Cain
pre .
mre .
enfants
nom Ida
pre .
mre .
enfants
Rdiger un programme principal qui cre la population illustre sur la figure, puis
imprime la liste des enfants de la grand-mre paternelle de Ida.
25
Hritage
Hritage
Un salari
nom
nombreEnfants
salaire
prime
Au dbut du corps dun constructeur de la classe drive, on peut appeler explicitement un constructeur de la classe de base avec les paramtres souhaits grce la
notation :
super(paramtres)
Si on omet cet appel, lexcution du constructeur est de toute faon prcde par
lexcution du constructeur sans paramtre de la classe de base.
Si une classe na aucun constructeur explicitement dfini, Java dfinit le constructeur
par dfaut, qui est sans paramtre, et qui, pour une classe hritire, consiste en lappel
du constructeur sans paramtre de la classe de base.
26
Hritage
package aa;
public class A {
...
protected int JJ;
...
}
class B extends A {
...
autoris
void PP() {
... JJ++; ... B b; ... b.JJ++;
...
A a; ... a.JJ++; ...
}
interdit
}
class C {
...
void QQ() {A a; ... a.JJ++; ...}
}
interdit
Q(s);
27
Hritage
Exercice 5
Une application de gestion de bibliothque doit manipuler des documents de natures
disparates, par exemple des livres et des dictionnaires. Tous les documents ont un
titre. Les autres attributs varient selon la catgorie du document : un livre possde un
auteur et un nombre de pages, un dictionnaire est caractris par le nombre de dfinitions de mots quil contient. Bien que de natures diverses, les documents doivent pouvoir tre manipuls de faon homogne en tant que simples documents, par exemple
pour en constituer des listes. On dfinit pour cela les classes Document, Livre et
Dictionnaire.
2 - Indiquer les lignes du programme suivant qui constituent des erreurs de syntaxe :
class TestBibli {
public static void main(String argv[]) {
Livre pereGoriot = new Livre(...);
Document doc; Livre livre;
doc = pereGoriot;
System.out.println(doc.titre);
System.out.println(doc.auteur);
livre = doc;
}}
28
Hritage
29
Hritage
class B extends A {
...
void P(){...}
...
void Q() {...}
}
Dans lexemple ci-dessus, la classe A annonce mais ne dfinit pas la mthode P. Cest
une classe abstraite. La classe drive B dfinit P et redfinit Q.
Une classe abstraite nayant pas toutes ses mthodes dfinies, il est interdit de crer
des objets de ce type.
Comme exemple simple, on peut considrer diverses catgories de figures : les cercles, les rectangles, les polygones. Toutes ces varits dobjets possdent la notion de
primtre. Cependant la ralisation de lopration est diffrente selon la catgorie.
Ajoutons cela que lon ne connat pas toutes les sortes de figures qui seront inventes par les programmeurs au moment o on conoit le type gnral des figures. Pour
cela, on indique, dans la classe Figure, que la fonction perimetre est abstraite.
abstract class Figure {
Point orig; // point origine de la figure
Figure(Point o) {orig=new Point(o);}
abstract double perimetre();
};
class Point { double X; double Y;
Point(double x, double y) {X=x; Y=y;}
Point(Point o) {X=o.X; Y=o.Y;}
};
class Cercle extends Figure {
private static final double pi=3.141592;
double rayon;
Cercle(Point centre, double r) {super(centre); rayon=r;}
double perimetre() {return 2*pi*rayon;}
}
30
Hritage
Cercle
rayon
perimetre
2rayon
Rectangle
virtuelle
Polygone
p1, p2, ...
perimetre
LX
LY
perimetre
...
di
2(LX+LY)
La classe gnrale Figure est abstraite, et on ne peut pas crer dobjets de cette
classe : sa seule raison dtre est de regrouper en une classe unique les diverses varits de figures.
4.3.2 Liaison dynamique des mthodes virtuelles
Chaque fois quune mthode perimetre() est appele sur un objet de type Cercle, Rectangle ou Polygone, cest la mthode du type prcis de lobjet qui est
excute. Ceci a lieu mme si la dsignation de lobjet prcis est de type vague. Le
compilateur ne peut pas dcider quelle mthode appeler ; seul un mcanisme dynamique peut appeler la bonne procdure au moment de lexcution. Cest ce dernier point
qui donne leur puissance aux mthodes virtuelles. Par exemple :
Cercle c= new Cercle(Point(4,10),12);
Rectangle r= new Rectangle(Point(10,110),20,30);
Figure f;
...
f= c; System.out.println(f.perimetre()); perim. de cercle
f= r; System.out.print(f.perimetre()); perim. de rectangle
31
Hritage
Hritage
33
class Complexe {
...
(1) void add(Complexe c1)
qui ralise this = this+c1
(2)
(3)
Exercice 7
Programmer les classes qui ralisent les listes ordonnes dentiers en considrant
deux reprsentations :
par un tableau de taille fixe au moment de la cration
par des maillons chans
Ces classes doivent offrir :
lajout dun lment, la valeur du ime lment (par ordre croissant), la longueur de la
liste, la saisie de la liste, son impression et la fusion de deux listes.
On essayera de profiter du fait que certaines de ces oprations sont exprimables en utilisant dautres oprations, ce qui permet de les programmer ds la dfinition de la
classe abstraite.
Remarque : la fusion doit permettre de fusionner une liste elle-mme, ce qui ncessite, pour viter tout effet de bord indsirable, de "cloner" la liste dans ce cas.
35
Interfaces - polymorphisme
Interfaces - polymorphisme
Certains langages, par exemple C++, permettent lhritage multiple : une classe peut
hriter de plusieurs classes. Ce nest pas le cas de Java : une classe ne peut hriter que
dune seule classe.
Cependant Java offre la notion dinterface, notion voisine, plus simple et suffisante
dans les cas usuels.
Une interface est une collection de dclarations de mthodes. On peut la considrer
comme un cas limite de classe abstraite : elle ne dfinit aucun corps de mthode, ni
aucun composant variable. Il ny figure que le profil des mthodes et ventuellement
des dclarations de constantes.
En plus de lhritage simple (mot cl extends), une classe peut implmenter une
ou plusieurs interfaces (mot cl implements), en dfinissant les mthodes de ces
interfaces.
Une interface sutilise comme une classe abstraite. tant donn une interface I, on
peut dclarer des variables, des rsultats ou des paramtres de type I. Tout objet instance dune classe qui implmente cette interface est compatible avec ces variables,
rsultats ou paramtres.
36
Interfaces - polymorphisme
En pratique, on utilise une interface chaque fois que cela suffit. Par exemple, le
regroupement de diverses catgories de figures peut donner lieu une interface qui
dclare les mthodes que doivent offrir toutes les figures, par exemple perimetre.
Lusage dune interface impose cependant de navoir aucune donne.
Dans lexemple suivant, linterface Figure remplace la classe abstraite Figure de
lexemple prcdent. On remarque quil nest plus possible dy incorporer le point
origine.
interface Figure {
double perimetre();
}
class Cercle implements Figure {
Point centre;
double rayon;
private static final double pi=3.141592;
Cercle(Point c, double r) {centre=new Point(c); rayon=r;}
public double perimetre() {return 2*pi*rayon;}
}
class Rectangle implements Figure {
Point coin;
double LX; double LY; // hauteur, largeur
Rectangle(Point p,double lx,double ly) {
coin=new Point(p); LX=lx; LY=ly;
}
public double perimetre() {return 2*(LX+LY);}
}
Lexemple suivant illustre comment lusage des interfaces permet de pallier labsence
dhritage multiple. Du point de vue logique, un polygone est une figure. Dun point
de vue de sa reprsentation, un polygone est une liste de points. On serait donc amen
faire hriter la classe polygone de la classe Figure et de la classe ListePoints. Lhritage multiple nexiste pas en Java, mais Figure tant une interface,
Polygone hrite de ListePoints et implmente Figure.
class ListePoints {
private int lng;
private Point P[]= new Point[100];
ListePoints() { lng=0;};
int longueur() {return lng;};
Point ieme(int i) {return P[i];};
void ajoutEnFin(Point p)
{if (lng<100) {P[lng]=p; lng=lng+1;}};
};
class Polygone extends ListePoints implements Figure{
Polygone (ListePoints lp) { super();
for(int i=0;i<lp.longueur();i++){ajoutEnfin(lp.ieme(i));
};
} ...
}
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999
37
Interfaces - polymorphisme
Polymorphisme
Le polymorphisme est la qualit de certaines fonctions, procdures ou classes de pouvoir sappliquer des paramtres de types divers dans la mesure o ces types disposent de certaines oprations dont le profil est fix et qui sont censes satisfaire
certaines proprits.
Les interfaces constituent un des moyens dexprimer le polymorphisme. Lexemple
suivant est un algorithme de tri gnrique. La procdure Algogene.tri tri un
tableau dobjets de type quelconque. La seule proprit exige est que les lments
soient comparables au moyen de la procdure inf(Object x, Object y) qui
rend vrai si x est infrieur (en un certain sens) y. Pour cela on utilise une interface
Compare qui dfinit le profil de la procdure inf. La procdure tri reoit en paramtre un objet comparateur op qui implmente cette interface.
interface Compare {
public boolean inf(Object x, Object y);
}
class AlgoGene {
public static void tri (Object [] T, Compare op) {
for (int i=T.length-1; i>=0; i--) {
for (int j=1; j<=i; j++) {
if (op.inf(T[j],T[j-1])) {
Object x=T[j-1]; T[j-1]=T[j]; T[j]=x;
}}}
}}
Pour trier nimporte quel type dobjets selon nimporte quel critre, il suffit dimplmenter linterface Compare au moyen dune classe convenable et de passer en paramtre du tri une instance de cette classe. Cest ce qui est propos dans lexercice
suivant.
Exercice 8
Des personnes sont caractrises par un nom, un ge et un poids :
class Personne {
public String nom; public int age; public int poids;
public Personne(String n, int a, int p){
nom=n; age=a; poids=p;
}}
On dfinit la population suivante :
Personne []
peuple[0] =
peuple[1] =
peuple[2] =
peuple[3] =
Utiliser AlgoGene pour trier cette population selon leur ge puis selon leur poids.
38
Interfaces - polymorphisme
Exercice 9
La classe Matrice ci-aprs offre la somme et le produit de matrices carres grce
aux mthodes statiques somme et produit.
class Matrice {
public Object [][] M;
public Matrice(int n) {M = new Object [n][n];}
public static Matrice produit(Matrice A,Matrice B,Ope op)
{
Matrice C =new Matrice(A.M.length);
for (int i=0; i<A.M.length; i++) {
for (int j=0; j<A.M.length; j++) {
C.M[i][j] = op.neutre();
for (int k=0; k<A.M.length; k++) {
C.M[i][j] =
op.add(C.M[i][j], op.mul(A.M[i][k],B.M[k][j]));
}}}
return C;
}
public static Matrice somme (Matrice A, Matrice B, Ope op)
{
Matrice C =new Matrice(A.M.length);
for (int i=0; i<A.M.length; i++) {
for (int j=0; j<A.M.length; j++) {
C.M[i][j] = op.add(A.M[i][j],B.M[i][j]);
}}
return C;
}}
Pour ne pas restreindre lutilisation des oprations sur matrices aux oprations numriques classiques, le type des lments nest pas fix (ils sont de la classe gnrale
Object) et les oprations utiliser pour raliser la somme et le produit des lments
sont transmises par le paramtre op des mthodes somme et produit.
Ces oprations sont :
add : la somme de deux lments,
mul : le produit de deux lments,
neutre : llment neutre de la somme.
Elles sont annonces au moyen de linterface Ope :
interface Ope
public Object
public Object
public Object
}
{
neutre();
mul(Object x, Object y);
add(Object x, Object y);
Ainsi le type des lments peut tre quelconque et les oprations ralises par les
algorithmes somme et produit peuvent tre diverses.
39
Interfaces - polymorphisme
Par exemple, on peut traiter les matrices dentiers de faon classique, les oprations
add, mul et neutre tant alors respectivement la somme des entiers, le produit des
entiers et lentier 0.
On peut galement utiliser une matrice dentiers G pour reprsenter un graphe, llment G(i, j) tant la distance du sommet i au sommet j, avec G(i, i)=0 et G(i,j)=infini
sil ny a pas darc de i vers j. Pour calculer les plus courts chemins, les oprations
add, mul et neutre doivent dans ce cas tre respectivement le minimum de deux
entiers, la somme des entiers et un entier plus grand que tout chemin dans le graphe
(par exemple 999). Avec ces oprations, lalgorithme bien connu de Warshall calcule
la matrice des plus courts chemins PCC selon la formule :
PCC = G + G2 +G3 + ... + Gn-1, n tant le nombre de sommets.
5
0
3
0
4
0
2
0
PCC =
4
0
7
3
1
5
3
0
4
2
9
7
4
0
6
3
9
6
2
0
G=
2
3
5
3
1
2
5
Les lments des matrices devant tre des objets (drivs de la classe Object), pour
des lments de type entier, on est oblig dutiliser la classe Int qui encapsule un
entier :
class Int { public final int val;
public Int(int v) {val=v;}
}
Dfinir la classe supplmentaire pour pouvoir raliser la somme et le produit usuel des
matrices. Rdiger un morceau de programme qui calcule le produit de deux matrices
M1 et M2 supposes dj construites et initialises.
Dfinir la classe supplmentaire pour pouvoir raliser lalgorithme de Warshall. Rdiger un morceau de programme qui, tant donn un graphe reprsent par une matrice
G de taille 5, dj construite et initialise, calcule la matrice PCC des plus courts chemins de ce graphe.
Remarque : G + G2 + G3 + G4 = (((G)G +G)G + G)G + G
40
TextComponent
TextField
texte dune ligne
Button
bouton
Checkbox
bote cocher
TextArea
texte gnral
Container
classe abstraite
composants qui en
contiennent dautres
Window
fentre de 1er niveau
Frame
fentre principale
Dialog
fentre de dialogue
Panel
zone dans un container,
regroupement de composants
ou zone de dessin
Lexemple suivant montre lutilisation de ces classes pour raliser un compteur command par linterface utilisateur illustre sur le dessin.
Cest une fentre dote de boutons
pour incrmenter un compteur, le
dcrmenter et quitter lapplication.
Une zone de texte affiche en permanence ltat du compteur.
1 - Cette fentre est ralise par la classe FenetreCompteur qui est une fentre de
1er niveau drive de Frame.
2 - Les trois boutons sont crs avec en paramtre le texte de leur tiquette.
3 - La zone de texte pour afficher le compteur est cre, avec une taille de 7 caractres.
4 - Pour chaque bouton, on dfinit une classe destine programmer la raction aux
vnements gnrs par le clic sur ce bouton. Ces vnements sont du type ActionEvent, et la mthode qui les traite est dfinie dans linterface ActionListener
et sappelle actionPerformed. Ces classes implmentent donc cette interface en
dfinissant le corps de la mthode actionPerformed.
5 - Le constructeur place les composants (boutons et zone de texte) lintrieur de la
fentre, au moyen de la procdure Placement dcrite plus loin.
6 - La mthode addActionListener de la classe Button indique quel est le
rcepteur des vnements associ au bouton. Ces rcepteurs doivent tre des objets de
type ActionListener. Ici ce sont des instances des classes dfinies en 4.
7 - La mthode pack() tasse au mieux les composants dans la fentre. setVisible(true) rend la fentre visible lcran (invisible par dfaut).
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999
41
Button boutonIncr=
Button boutonDecr=
Button boutonQuit=
new Button("+");
new Button("-");
new Button("quit");
3
class ActionIncr implements ActionListener {
public synchronized void actionPerformed(ActionEvent e)
{compteur ++; afficherCompteur();}
};
class ActionDecr implements ActionListener {
public synchronized void actionPerformed(ActionEvent e)
{compteur --; afficherCompteur();}
};
6
7
prioritaire non logiquement bloqu. Ceci signifie quil est impratif que main() termine, ou bien baisse sa priorit, pour que lapplication ne soit pas bloque. Dans
lexemple, main() termine aprs avoir cr un objet FenetreCompteur. Lapplication vit ensuite au titre du processus unique de gestion de lenvironnement, par les
appels de mthodes engendrs par les vnements.
Chaque classe dvnement xxxEvent est accompagne dune interface xxxListener qui dfinit les mthodes de raction ces vnements.
ActionListener:
MouseListener :
KeyListener
actionPerformed(ActionEvent)
mouseClicked(MouseEvent)
mouseDragged(MouseEvent)
mouseMoved(MouseEvent)
keyPressed(KeyEvent)
keyReleased(KeyEvent)
keyTyped(KeyEvent)
etc ...
Pour rcuprer et traiter les vnements de type xxxEvent gnrs par un composant comp il faut :
Dfinir une classe TraiteEvenementsDeComp qui implmente xxxListener. On programme le traitement dun vnement dans le corps dune
mthode de cette classe.
Inscrire un objet de type TraiteEvenementsDeComp auprs du composant
comp pour que ce dernier appelle cet objet loccasion de chaque vnement.
Cette inscription se fait au moyen de la mthode addxxxListener(xxxListener). Cela se fait souvent dans le constructeur de la fentre
qui contient le composant comp :
comp.addxxListener(new TraiteEvenementsDeComp)
43
45
46
class Color {
Color(int r, int g, int b); composantes rgb de la couleur
Color(int code);
final static Color black, blue, cyan, darkGray, gray,
green, lightGray, magenta, orange,
pink, red, white, yellow;
couleurs usuelles
...
}
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999
47
Pour exprimer des traitements parallles, Java offre le moyen de crer des processus.
Ces processus communiquent en partageant des objets et se synchronisent au moyen
de moniteurs qui sont une variante des moniteurs de Hoare.
Dans lexemple prcdent, chaque processus est cr avec un modle qui lui est propre, la classe A et la classe B. On peut crer plusieurs processus sur un mme modle,
avec ventuellement des paramtres de cration, comme le montre la version suivante,
totalement quivalente lexemple prcdent :
class Impr extends Thread {
String txt; int periode;
public Impr(String t, int p){txt=t; periode=p;}
public void run() {
for(int i=0; i<8; i++) { System.out.print(txt+i+" ");
try {sleep(periode);}catch(InterruptedException e){};
};
}
}
public class TestThread2 {
static public void main(String argv[]) {
new Impr("A",100).start(); new Impr("B",200).start();
} }
49
Pour assurer les exclusions, Java offre la primitive synchronized. En Java tout
objet est susceptible dtre un motif dexclusion. La syntaxe gnrale dune exclusion relative un objet obj est :
synchronized(obj) { bloc dinstructions }
Cette construction assure que ce bloc dinstructions nest pas excut avant que toute
excution en cours sous la coupe dun synchronized(obj) ne soit acheve.
On peut alors programmer comme suit lexclusion souhaite :
class Exclusion {};
class PusImpr2 extends Thread {
String txt;
static Exclusion exclusionImpression = new Exclusion();
public PusImpr2(String t){txt=t;}
public void run() {
for(int j=0; j<2; j++) {
synchronized(exclusionImpression){
for(int i=0; i<txt.length(); i++) {
try {sleep(100);} catch(InterruptedException e) {};
System.out.print(txt.charAt(i));
};
System.out.print(" ");
};
};
}
}
On a dfini une classe Exclusion, dont le seul rle est de permettre de crer de
purs objets dexclusion. Un objet exclusionImpression de type Exclusion est utilis comme argument de synchronized afin dassurer lexclusion sur
limpression pour la dure de la boucle dimpression. Cette version imprime un rsultat plus convenable :
BONJOUR AU REVOIR BONJOUR AU REVOIR
Remarque : la classe Exclusion peut sembler artificielle. Elle est vide, sans aucun
membre de donne ni aucune mthode. Elle nest pas si artificielle que cela. En fait
elle modlise lessence mme de ce quest un objet : sa principale proprit est dtre
gal lui-mme et diffrent des autres et cest effectivement la seule proprit dun
motif dexclusion.
50
8.2.2 Moniteurs
Lexclusion peut porter sur une mthode complte. La syntaxe est dans ce cas :
class A {
...
synchronized ... P(...) { ... }
...
}
Le motif dexclusion est alors lobjet courant de classe A, et lexclusion est garantie
pour toute lexcution de la mthode. La classe A est ainsi un modle de moniteur (au
sens de Hoare) et les mthodes dotes de lattribut synchronized sont les entres
dun tel moniteur.
Quand cest possible, et cest pratiquement toujours le cas, il est prfrable dutiliser
cette forme, qui force structurer lapplication de telle manire que les problmes de
synchronisation relatifs un mme thme soient centraliss au sein dun moniteur,
plutt que disperss dans plusieurs morceaux de textes. Le motif dexclusion est alors
un moniteur dont les membres de donnes constituent tout ou partie de ltat de la
chose partage et dont les mthodes sont les actions excuter en exclusion. Pour
lexemple prcdent, cela donne :
class MoniteurImpression {
synchronized void imprTexte(String txt) {
for(int i=0; i<txt.length(); i++) {
try {Thread.sleep(100);}
catch(InterruptedException e) {};
System.out.print(txt.charAt(i));
};
}
}
51
Le modle de tampon est mis en uvre par la classe MoniteurProdCons qui offre
deux entres, prod() pour produire et cons() pour consommer. Un processus qui
veut produire est mis en attente si le tampon est plein, un processus qui veut consommer est mis en attente si le tampon est vide.
Une utilisation possible est illustre ci-dessous. La classe Producteur est un
modle de processus qui produit trois messages, et la classe Consommateur est un
modle de processus qui consomme trois messages. On a ajust le dbit de la production au moyen de sleep() de faon avoir un comportement non trivial dans lequel
apparaissent des attentes aussi bien du producteur que du consommateur.
class Producteur extends Thread {
MoniteurProdCons tampon;
public Producteur (MoniteurProdCons t) { tampon=t;}
public void run() {
tampon.prod("message1"); tampon.prod("message2");
try {sleep(100);} catch(InterruptedException e) {};
tampon.prod("message3");
}}
class Consommateur extends Thread {
MoniteurProdCons tampon;
public Consommateur(MoniteurProdCons t) { tampon=t;}
public void run() {
tampon.cons(); tampon.cons(); tampon.cons();
}}
Le programme principal cre un tampon et une paire de processus producteur/consommateur qui communiquent par ce tampon :
public class TestWait {
static public void main(String argv[]) {
MoniteurProdCons tampon = new MoniteurProdCons();
new Producteur(tampon).start();
new Consommateur(tampon).start();
}}
Lexcution de cet exemple donne la trace suivante :
PRODUIT
: message1
PRODUCTEUR ATTEND
CONSOMME : message1
PRODUIT
: message2
CONSOMME : message2
CONSOMMATEUR ATTEND
PRODUIT
: message3
CONSOMME : message3
Remarque : le tampon de lexemple prcdent ne fonctionne que sil nexiste quun
seul producteur et un seul consommateur pour un tampon (voir exercice suivant).
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999
53
Exercice 10
Donner un exemple de scnario qui montre que pour plus dun producteur et/ou plus
dun consommateur pour un mme tampon, la programmation prcdente du tampon
est incorrecte.
Donner une version de tampon une place qui soit correcte pour un nombre quelconque de producteurs et de consommateurs.
8.3.3 Utilisation - Analogie avec les Moniteurs de Hoare
Ce mcanisme constitue un cas particulier des conditions offertes par les moniteurs
de Hoare. Un moniteur de Hoare permet de dclarer plusieurs conditions c1, c2 ... sur
lesquelles les processus peuvent attendre par attendre(ci), et tre relancs par reprendre(ci).
Du point de vue mthode de programmation, les conditions sutilisent ainsi : si une
certaine condition logique, exprime par un prdicat P concernant ltat doit tre
satisfaite pour quune action puisse tre poursuivie, on associe une condition cp ce
prdicat. Aux endroits du programme o P doit tre satisfait, on crit quelque chose
comme :
... si non P alors attendre(cp) fsi; /* P est vrai */ ...
et aux endroits o on sait que P devient vrai, on crit :
... /* P est vrai ici */ reprendre(cp) ...
Si ceci est partout respect, cest--dire si P est vrifi devant tout reprendre(cp),
lassertion P est bien videmment vrifie derrire tout attendre(cp) sous rserve que
le langage assure le passage du contrle (squentiel, car on est au sein de lexclusion
du moniteur) aussitt au processus rveill. Dun point de vue de la preuve formelle,
la condition cp agit comme un tuyau qui transmet la vrit de lassertion P entre
divers points du texte de programme.
La diffrence essentielle avec les moniteurs de Java est que ces derniers ont une seule
condition par moniteur, non dclare car dfinie implicitement. Avec une seule condition, on se ramne au cas gnral en re-testant aprs chaque attente que le prdicat P
est vrai et en se remettant en attente sinon. La programmation devient quelque chose
comme :
... while (!P) {wait();} /* P est vrai */ ...
et aux endroits o une assertion attendue devient vraie :
... /* P ou Q ou R ou ... */ notify() ou notifyAll(); ...
Le choix entre notify() et notifyAll() nest pas toujours vident. Si on respecte strictement la mthode suggre ici, on peut toujours utiliser notifyAll() :
cela risque simplement de conduire de mauvaises performances cause de rveils
inutiles de processus.
Allocation du moniteur aprs notify() :
Aprs un notify(), plusieurs processus sont concurrents pour utiliser le moniteur.
Cependant le langage doit assurer le maintien de lexclusion sur ce moniteur (cest
obligatoire si on veut pouvoir affirmer quoi que ce soit sur ltat du moniteur). Avec
54
55
Interactivit :
Lexemple suivant illustre lusage des processus pour raison dinteractivit. Il offre la
possibilit de lancer, depuis une fentre de contrle, la paye du personnel tout en
permettant loprateur de faire de ldition de texte.
La paye du personnel est programme sous la forme dun modle de processus indpendant (1). Le clic sur bouton Paye du personnel cre un tel processus (1).
On aurait pu galement programmer ldition de texte sous la forme dun processus
indpendant. Cela nest pas ici ncessaire car sa logique est suffisamment simple pour
tre programme entirement dans les procdures de raction aux vnements des
boutons copier et coller. Lditeur de texte (3) est donc simplement un objet passif qui
sexcute au titre du processus de gestion des vnements.
La programmation de cet exemple est un peu fallacieuse, car le mcanisme dallocation du processeur aux processus est gnralement sans partage de temps, avec allocation au processus non logiquement bloqu de plus forte priorit. Dans ce cas, si la
paye du personnel na pas loccasion de se bloquer et si cest un processus de mme
priorit que la gestion des vnements, lditeur de texte ne pourra pas sexcuter. Ici
cela marche car on utilise sleep() dans la boucle du programme de paye. Dans un
cas rel, il faudrait lancer la paye du personnel avec une priorit plus faible que la gestion des vnements, en remplaant (1) par :
PayeDuPersonnel p= new PayeDuPersonnel();
p.setPriority(Thread.currentThread().getPriority() - 1);
p.start();
57
58
class Edit {
59
2
public void cercle()
61
class Cercle {
public int Xc; public int Yc; public int rayon;
class DialogueDeCercle extends Frame {
TextField txtXcentre= new TextField(5);
TextField txtYcentre= new TextField(5);
TextField txtRayon = new TextField(5);
Button ok=
new Button("OK");
class ActionOk implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
reprendre();
}
3
}
public DialogueDeCercle() {
super("CERCLE");
Placement.p(this, new Label("Xc"), 0,0,1,1);
Placement.p(this, txtXcentre,
0,1,1,1);
Placement.p(this, new Label("Yc"), 1,0,1,1);
Placement.p(this, txtYcentre,
1,1,1,1);
Placement.p(this, new Label("Rayon"), 2,0,1,1);
Placement.p(this, txtRayon,
2,1,1,1);
Placement.p(this, ok, 1,2,1,1);
ok.addActionListener(new ActionOk()); pack(); setVisible(true);
attendre();
}
public synchronized void reprendre() { notify();}
public synchronized void attendre() {
try {wait();} catch(InterruptedException e){};
}
}
public Cercle() { // constructeur, saisie du cercle par dialogue
DialogueDeCercle dial= new DialogueDeCercle();
Xc=Integer.parseInt(dial.txtXcentre.getText());
5
Yc=Integer.parseInt(dial.txtYcentre.getText());
rayon=Integer.parseInt(dial.txtRayon.getText());
dial.dispose();
}
public void dessine(Graphics g) {
g.drawOval(Xc-rayon,Yc-rayon,2*rayon,2*rayon);
}
}
62
Une programmation uniquement faite laide de procdures de ractions est beaucoup plus lourde : une telle procdure ne peut attendre, elle est donc oblige de noter
ce qui reste faire. Dans cet exemple, la procdure de raction au clic sur Cercle
doit se limiter crer le dialogue de saisie du cercle. Mais que faire de ce cercle quand
le dialogue la construit, lorsque loprateur clique OK ? Qui aura not quon se
trouve dans la situation capte en (2) par le contrle ? Comment sait-on que ce cercle
est destin tre ajout cette scne, et dessin sur sa zone de dessin ? Dans cet
exemple, pourtant simpliste, il faudrait :
1/ Remplacer la mthode cercle() de la classe Scene par deux mthodes :
debutCercle() qui cre un dialogue de cercle pour la saisie, et finCercle(Cercle cercleSaisi), appele lors du clic sur OK et qui poursuit lactivit au sujet de ce cercle, savoir lajouter la scne et le dessiner.
2/ Lors de la cration du dialogue de cercle, il faut lui rvler la mthode finCercle associe cette scne, de sorte quelle soit accroche comme procdure de raction au clic sur OK. On peut faire cela au moyen dun objet de type
ActionListener membre de la scne que lon passe en paramtre linitialisation
du dialogue de cercle.
Le lecteur conviendra que cette faon de faire est peu raisonnable.
Calcul parallle
Lexemple suivant est un exemple simple de calcul parallle. Il sagit dune recherche
dans une table associative. On a prvu de faire la recherche au moyen de plusieurs
processus qui se partagent la recherche dans des portions disjointes de la table.
TableAssoc
processus
PusRecherche
mthode
moniteur
P1
rechercheParallele
Resul
attente rsultat
P2
wait()
notify()
signalement
rsultat
P3
La mthode rechercheParallele cre, en (3), un moniteur resul pour se synchroniser sur lobtention du rsultat et, en (4), des processus de type PusRecherche, chacun initialis avec les indices de dbut et de fin de la portion de table qui leur
est attribue, puis en (5) elle attend le rsultat pour le rendre.
Le modle de processus PusRecherche est donn en (1). Si la cl est trouve, il
transmet la valeur associe par la mthode delivre du moniteur resul, sinon il le
signale par la mthode delivreRien.
Le type de moniteur Resul est prsent en (2). La mthode delivre rveille
lattendeur. La mthode delivreRien ne rveille lattendeur que si aucun processus na trouv la cl, ce qui se dtecte en comptant le nombre de processus qui nont
rien trouv. Pour une cl qui ne figure pas dans la table, il est convenu que le rsultat
soit null.
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999
63
class TableAssoc {
class Paire {
String cle; String valeur;
Paire(String c, String v) {cle=c; valeur=v;}
}
Paire T[]; int nbAssoc;
public TableAssoc(int tailleMax) { T= new Paire[tailleMax]; nbAssoc=0;}
public void associer(String c, String v) {
T[nbAssoc]= new Paire(c,v); nbAssoc++;
}
1
class PusRecherche extends Thread {
int debut; int fin; String cle;
PusRecherche(int d, int f, String c) {debut=d; fin=f; cle=c;}
public void run() {
for (int i=debut; i<fin; i++) {
if (cle.equals(T[i].cle)) { resul.delivre(T[i].valeur); return;}
};
resul.delivreRien();
}
}
class Resul { // moniteur de delivrance du resultat de recherche
int nbPus; // nombre de processus en cours sur la recherche
String valeurTrouvee;
Resul(int nbp) { nbPus = nbp; valeurTrouvee = null;}
2
synchronized void delivre(String v) {
valeurTrouvee = v; notify();
}
synchronized void delivreRien() {
nbPus--; if(nbPus==0) {notify();}
}
synchronized String attend() {
try {wait();} catch(InterruptedException e){}; return valeurTrouvee;
}
}
Resul resul;
64
65
Une applet est un programme Java qui peut tre rfrenc depuis un document
HTML. Elle est destine tre tlcharge et excute par le navigateur Web lorsque
ce document HTML est sollicit.
Pour programmer une applet, il suffit de driver la classe Applet dfinie dans le
paquetage java.applet.
init();
start();
paint(Graphic g);
stop();
destroy();
66
graphique de lapplet lorsque laffichage doit tre modifi. Cette mthode appelle
paint(Graphics g) avec le bon paramtre g.
La mthode stop() est appele chaque fois que le navigateur quitte le document.
La mthode destroy() est appele lorsque lapplet est dtruite, ce qui se passe
gnralement lorsque le navigateur termine une session.
void setColor(Color c)
Color getColor()
assigne/obtient la couleur courante de dessin
void setFont(Font f)
Font getFont()
assigne/obtient la fonte courante de dessin de texte
boolean drawImage(Image im, int x,int y, ImageObserver o)
affiche limage im dans le rectangle (x,y,w,h). Le paramtre o est utile pour
des images longues charger. La mthode drawImage() peut tre
applique alors que limage im nest pas totalement charge, et o est lentit
qui doit tre prvenue lorsque laffichage est achev. Usuellement o est
lobjet de classe Component dans lequel limage est affiche.
...
}
Lexemple suivant illustre une applet qui affiche un petit chronomtre qui sincrmente chaque seconde :
67
68
La classe Applet permet laffichage dimages (fichiers .gif ou .jpg) et lmission de sons (fichiers .au).
La mthode Image getImage(...) rend une reprsentation interne dune image
contenue dans un fichier.
On peut afficher une image im par g.drawImage(im,....) , g tant le contexte
graphique associ lapplet.
La mthode AudioClip getAudioClip(...) rend une reprsentation interne
dun enregistrement sonore contenu dans un fichier.
On peut jouer un enregistement sonore m par m.play().
Exercice 11
Programmer une applet dont le comportement est illustr ci-dessous :
boing
Priodiquement, lapplet affiche une ellipse aplatie pendant 0,5 seconde puis une
ellipse plus enfle tout en prononant boing au moyen dun fichier de son
boing.au.
69
Lmission et la rception de paquets se font par lintermdiaire dun socket, reprsent en Java par un objet de classe DatagramSocket :
class DatagramSocket {
DatagramSocket() throws SocketException;
DatagramSocket(int port) throws SocketException;
int getLocalPort();
void send(DatagramPacket p);
void receive(DatagramPacket p);
}
Le constructeur DatagramSocket()cre un socket associ un numro de port
disponible dcid par le systme. Le constructeur DatagramSocket(int port)
impose le numro de port indiqu. La mthode getLocalPort() permet de connatre le numro du port associ.
La mthode send(DatagramPacket p) envoie le paquet p.
La mthode receive(DatagramPacket p) range le prochain paquet reu dans
lobjet p.
Les programmes suivants illustrent la communication entre des questionneurs (classe
Questionneur) et un rpondeur (classe Repondeur). Les questionneurs posent
des questions. Le rpondeur reoit les questions et y rpond. Questions et rponses
sont des chanes de caractres frappes au clavier et valides par passage la ligne :
Le site o sexcute le rpondeur sappelle un site serveur : son adresse est a priori
connue des questionneurs. Dans lexemple, il sagit de la machine florence.irisa.fr. Un site o sexcute un questionneur est un site client : son
adresse est initialement inconnue du site serveur. Le serveur en prend connaissance
par les paquets quil reoit.
71
72
public Repondeur() {
try{ socketClient = new DatagramSocket(portRepondeur);}
catch(SocketException e){}
while (true) {
try{socketClient.receive(paquetQuestion);}
catch(Exception e){return;};
adrClient = paquetQuestion.getAddress();
portClient = paquetQuestion.getPort();
System.out.println(new String(paquetQuestion.getData()));
byte strb[]= Lecture.chaine("\n").getBytes();
byte sbuf[]=new byte[1000];
for (int i=0;i<strb.length;i++){sbuf[i]=strb[i];};
DatagramPacket paquetReponse =
new DatagramPacket(sbuf,100,adrClient,portClient);
try {socket.send(paquetReponse);} catch(Exception ex){};
}
}
Exercice 12
Modifier la programmation de la classe Repondeur de faon ce que la question
affiche soit prfixe par lidentification de la machine cliente, sous la forme :
nom de la machine cliente : question
73
class Socket {
Socket(String host, int port);
Socket(InetAddress adr, int port);
InetAddress getInetAddress();
InputStream getInputStream();
OutputStream getOutputStream();
void close();
}
La classe Socket ne suffit pas car ltablissement dune connexion est toujours disymtique : il y a un ct serveur, qui prexiste, dont ladresse est suppose connue, et
qui attend des clients, et un ct client qui a linitiative de ltablissement de la connexion. Ceci ncessite une seconde sorte dobjet ct serveur, un ServerSocket,
dont le rle est dtre lcoute des clients.
class ServerSocket {
ServerSocket(int port);
InetAddress getInetAddress();
Socket accept();
void close();
}
Le mcanisme dtablissement dune connexion est illustr sur le schma suivant :
(1) : lactivit serveur cre un ServerSocket sur le site serveur.
(2) : lactivit serveur appelle la mthode accept() de ce ServerSocket, ce qui
la met en attente dune manifestation de la part dun client.
(3) : un site client cre un Socket en citant ladresse et le port du serveur. Ceci sollicite le ServerSocket associ cette adresse et ce port. Le ServerSocket cre
alors un Socket sur le site serveur et le connecte ce client.
(4) : La mthode accept() termine en rendant ce Socket.
(5) : Le client et le serveur peuvent alors dialoguer grce aux flux dentre et de sortie
offerts par les mthodes getInputStream() et getOutputStream() de leur
Socket respectif.
site serveur
Socket
site client
Socket
activit
serveur
5
ServerSocket
4
2
3
adrServeur
portServeur
accept
75
Exercice 13
La programmation prcdente du questionneur et du rpondeur est simple mais peu
illustrative dune relation entre des clients et un serveur. Le propre des clients est
dtre potentiellement nombreux et, dans la mesure o un service est long et interactif,
il est souhaitable que les clients soient servis en parallle.
Programmer un site serveur qui offre un service de rpondeur plusieurs clients
simultanment. Sur le site serveur, chaque service rpondeur en cours donnera lieu
une fentre dote dune zone de texte pour afficher la question, une zone de texte pour
prparer la rponse et un bouton pour envoyer la rponse :
site serveur
site client
site client
76
class URL {
URL(String url);
Socket(InetAddress adr, int port);
String getFile();
String getHost();
InputStream openStream();
URLConnection openConnection();
}
Une faon simple de lutiliser est de crer un objet URL au moyen du constructeur
avec lURL en paramtre, sous forme dune chane de caractres de la forme
http://...
puis dutiliser la mthode openStream() pour obtenir un objet de type InputStream qui permet de lire le contenu de cette url.
Lexemple suivant est un programme qui lit au clavier un nom dURL, suppos dsigner un fichier texte, et limprime sur le terminal :
77
site serveur
site client
Calculateur
...
somme(12,34)
...
"rsultat:46"
somme(int x,int y)
e103c04.ifsic.univ-rennes1.fr:8090
}
78
Cest peu prs la seule opration de gourou effectuer. Le reste est relativement
clair :
lance, sur le site serveur, un processus
destin enregistrer les associations, au titre du port 8090, entre des adresses externes
et les objets que lon dsire rendre accessibles de cette manire.
LocateRegistry.createRegistry(8090)
79
4/ Connexion dun site client un objet distant connu par son nom externe
Le programme ci-dessous, excut sur un site client, obtient la rfrence un objet
distant connu par son adresse externe grce :
Naming.lookup()
Cette mthode rend une rfrence non type. Il faut donc la typer convenablement au
moyen dun cast.
Ensuite on peut utiliser la rfrence ainsi rendue comme si lobjet rfrenc tait un
objet local. Lors dune invocation de mthode, le systme sous-jacent soccupe de
transformer le passage des paramtres et le rendu de rsultat en envoi de messages
vers le site serveur et rception de messages depuis le site serveur.
import java.rmi.*; import java.rmi.registry.*;
import es.*;
public class ClientCalcul {
public static void main(String argv[]){
try {
Calculateur calc =
(Calculateur) Naming.lookup(
"//e103c04.ifsic.univ-rennes1.fr:8090/Calculateur");
while (Lecture.unCarCmde() != '.') {
int x = Lecture.unEntier();
int y = Lecture.unEntier();
System.out.println(calc.somme(x,y));
}
}
catch(Exception e){
System.out.println("erreur"); System.exit(0);
}
}
}
serveur
stub
skeleton
communications rseau
80
Ainsi, en rgle gnrale, dans une application rpartie, seuls quelques objets sont connus par une adresse externe dfinie statiquement, par exemple un ou quelques objets
serveurs particuliers. Ces objets prexistants sont le germe dactivits qui crent
dynamiquement des objets et se les communiquent par paramtres ou rsultats de procdures, de faon tout fait conventionnelle, comme si ces objets taient situs sur le
mme site.
Lexemple suivant illustre un schma frquent : un site client 1 sadresse un serveur
serv qui est un objet connu par une adresse externe "//machine/8080/serv"
en appelant une mthode (1) qui cre un objet spcifique service1, dont la rfrence est rendue en rsultat au client. Ensuite, (2) client 1 dialogue directement avec
cet objet service1. Et ce principe est reproductible pour autant de clients que lon
veut.
81
site client 1
site serveur
service1
1
site client 2
service2
2
1
new
serv new
//machine/8080/serv
Exercice 14
Programmer, comme dans lexercice 13, mais cette fois au moyen dappels de mthodes des objets distants, un site serveur qui offre un service de rponse plusieurs
clients simultanment. Sur le site serveur, chaque service de rponse un nouveau
client donne lieu une nouvelle fentre :
site serveur
Chaque client dialogue au moyen dune fentre qui affiche question et rponse. Un
bouton supplmentaire FIN permet de clore la session de dialogue de ce client avec le
serveur. La clture de session doit faire disparatre la fentre correspondante sur le site
serveur.
On peut prvoir larrt du serveur au moyen dun bouton ARRET DU SERVEUR.
82
return(
titre dexercice amusant, le lecteur pourra essayer de transmettre larbre gnalogique de lexercice 4 et vrifier que cela fonctionne.
83
12
84
TP1
Gestion des tudiants
Rappel :
On doit grer des notes dtudiants dans diverses matires. Un tudiant possde un
nom et une adresse.
Chaque tudiant est reprsent par un objet unique, maintenu dans une liste, la liste de
tous les tudiants.
On doit pouvoir ajouter un tudiant la liste des tudiants, modifier ladresse dun
tudiant, obtenir un tudiant partir de son nom.
Pour chaque matire, certains tudiants possdent une note. Pour une matire donne,
on doit pouvoir ajouter un tudiant et sa note, demander la note dun tudiant, modifier la note dun tudiant, calculer la moyenne des notes, imprimer la liste des notes
avec le nom et ladresse des tudiants.
85
TP2
Deux reprsentations des matrices
On veut dfinir le type Matrice, matrices carres de taille fixe N, dont les lments
sont des nombres flottants. On dsire offrir deux mises en uvre :
laide dun tableau deux dimensions (dclaration : float T[N][N]),
laide dune liste dlments de la forme <i-ligne, i-colonne, valeur>, les l-
86
TP3
Simulateur de circuits logiques
On dsire programmer un simulateur (simple) de circuits logiques, en utilisant au
mieux les notions gnrales de programmation par objets de Java.
Description de lapplication
Les circuits logiques sont soit des circuits de base (par exemple inverseur INV, porte
et ET, ...) soit des circuits composs, rsultant dun assemblage dautres circuits.
Les composants dun circuit compos sont relis entre eux par des fils : par exemple,
le circuit compos NON_ET utilise le fil f pour connecter la sortie dune instance de
ET lentre dune instance de INV.
Les broches, fils dentre et de sortie dun modle de circuit, permettent de connecter
une instance de circuit des fils effectifs qui le relient dautres circuits.
circuits de base
e1
e s
s
e2
INV
ET
circuits composs
ma0
f1
NON_ET
f3
e1
NON_ET
VERROU
f
NON_ET
e2
s
ma1
f2
87
Une analyse du problme a conduit aux remarques suivantes, qui doivent servir de
guide la rdaction du logiciel :
Les circuits et les fils ont des caractristiques qui en font tout naturellement des
objets.
Les circuits composs sont composs de circuits et de fils.
Les broches dun circuit dsignent les fils qui relient ce circuit dautres circuits. Cette dsignation peut avantageusement tre installe lors de la cration
du circuit, au moyen des fils effectifs dinterconnexion passs en paramtre du
constructeur.
Pour effectuer la simulation, le simulateur doit avoir accs la liste de tous les circuits
de base et la liste de tous les fils. Il est judicieux de faire de ces listes des membres
statiques dans lesquelles chaque objet, circuit de base ou fil, se rajoute lors de sa
cration.
On dispose de la classe ListPrcr, liste dobjets de type quelconque (object),
dote dun tat de parcours permettant de grer un parcours au moyen des mthodes
debut(), avance(), et estEnFin(). Cette classe est dfinie dans le paquetage
list.
1re partie
Rdiger les classes ncessaires pour disposer des circuits suivants :
circuits de base INV et ET,
circuits composs NON_ET et VERROU.
88
2me partie
Rdiger le simulateur. Cest une classe SimulVerrou que lon doit rendre indpendante de toute interface utilisateur particulire. Pour cela elle disposera de deux fournisseurs de bits pour lire les squences de valeurs placer sur les fils dentre ma0 et
ma1 et de trois afficheurs de bits pour afficher les valeurs des entres ma0, ma1 et
de la sortie s. Ces fournisseurs de bits et ces afficheurs de bits sont passs en paramtre du constructeur du simulateur.
Le fonctionnement du simulateur est assur par une mthode top() qui assure une
tape de simulation. La simulation complte se fait par une squence dappels la
mthode top().
ma0
VERROU
ma1
3me partie
Raliser linterface utilisateur illustre sur la figure. Deux botes cocher (CheckBox) servent indiquer la valeur prsente de ma0 et ma1 (relch signifie 0 et
enfonc signifie 1. Rien nest prvu pour injecter la valeur indtermine X). Un bouton top sert provoquer une tape de simulation. Trois zones de texte servent afficher la suite des valeurs de ma0, ma1 et s. Un bouton quit permet de terminer
lapplication.
89
TP4
Tri parallle
On se propose de programmer en Java une trieuse systolique. Elle trie des donnes
prsentes squentiellement, en un temps proportionnel au nombre de donnes.
Principe de fonctionnement
La trieuse est constitue demplacements, comportant chacun deux registres appels
min et max. Tous les registres sont initialiss avec la valeur infinie. Ensuite le tri
se droule en deux phases :
1) Introduction des donnes : les donnes des registres max sont dcales vers la
droite en introduisant une nouvelle donne dans lemplacement de gauche et les donnes max et min de chaque emplacement sont changes si max<min.
2) Sortie des valeurs tries : la donne min de lemplacement de gauche est lue, les
donnes des registres min sont dcales vers la gauche en introduisant la valeur
infinie dans lemplacement de droite et les donnes max et min de chaque emplacement sont changes si max<min.
Exemple : tri de la liste de donnes 12, 18, 14, 27, 4, 60.
Introduction des donnes
12
27
18
12
14
18
60
12
12
14
18
12
27
60
14
27
14
18
12
18
27
14
12
18
14
27
60
18
60
27
60
12
18
12
14
27
14
18
18
27
60
27
60
90
91
TP5
Applet
On se propose de programmer une applet pour apprendre compter. Le programme
tire deux nombres i et j au hasard compris entre 0 et 9 et pose vocalement la question
i plus j.
Lutilisateur peut donner la rponse en cliquant sur un des 19 boutons prvus cet
effet. Si la rponse est correcte, le programme rpond exact, sinon il rpond non, la
rponse est i+j. Si lutilisateur ne donne pas de rponse au terme dun dlai de 5
secondes, le programme donne la bonne rponse la rponse est i+j. Toutes les
rponses du programme sont donnes oralement.
On dispose pour cela des fichiers audio :
n1.au, n2.au, ... n18.au
pour prononcer les nombres
et
plus.au, exact.au, non.au, laReponse.au
pour prononcer les mots correspondants.
92
TP6
Communications rseau
Dans une relation clients-serveur, le propre des clients est dtre potentiellement nombreux et, dans la mesure o un service est long et interactif, il est souhaitable que les
clients soient servis en parallle.
Programmer un site serveur qui offre un service de rpondeur plusieurs clients
simultanment. Sur le site serveur, chaque service rpondeur en cours donnera lieu
une fentre dote dune zone de texte pour afficher la question, une zone de texte pour
prparer la rponse et un bouton pour envoyer la rponse :
site serveur
site client
site client
93
TP7
Application rpartie - RMI
Programmer au moyen dappels de mthodes des objets distants, un site serveur qui
offre un service de rpondeur plusieurs clients simultanment. Sur le site serveur,
chaque service de rponse un nouveau client donnera lieu une nouvelle fentre :
site serveur
94
CORRIGS dEXERCICES
et de
TRAVAUX PRATIQUES
mars 1999
95
Corrig 1.
class PileEnt {
private Maillon sommet;
private class Maillon {
int elt; Maillon suiv; Maillon(int e, Maillon s){elt=e; suiv=s;}
};
public PileEnt() {sommet=null;}
public void empiler(int e) {sommet= new Maillon(e,sommet);}
public void depiler() {sommet=sommet.suiv;}
public int sommet() {return sommet.elt;}
public class Parcours {
private Maillon courant;
public Parcours() {courant=sommet;}
public Parcours(Parcours p) {courant=p.courant;}
public int element() {return courant.elt;}
public void suivant(){courant=courant.suiv;}
public boolean estEnFin(){return courant==null;}
}
}
class ParcPile {
public static void main(String argv[]) {
PileEnt p= new PileEnt();
p.empiler(7); p.empiler(5); p.empiler(4); p.empiler(6);
p.empiler(5); p.empiler(2); p.empiler(1); p.empiler(3);
boolean trouve = false;
PileEnt.Parcours parc1= p.new Parcours();
while (!parc1.estEnFin() & !trouve) {
int e=parc1.element();
PileEnt.Parcours parc2= p.new Parcours(parc1);
parc2.suivant();
while (!parc2.estEnFin() & !trouve) {
if (parc2.element()== e){
System.out.println("element double : " + e + "\n");
trouve=true;
};
parc2.suivant();
}
parc1.suivant();
}
}
}
96
Corrig 2.
ListAssoc() {};
boolean estVide() {return tete==null;}
void ajoute(String c1, String c2) {
= new Maillon(c1,c2,tete);
97
Corrig 3
98
Corrig 4
Arbre gnalogique
class ListePersonnes {
private class Maillon {
Personne pers;
Maillon suivant;
Maillon(Personne p, Maillon s) { pers=p; suivant=s;}
}
private Maillon tete; private Maillon courant;
ListePersonnes() {};
boolean estVide() {return tete==null;}
void ajoute(Personne p) { tete = new Maillon(p,tete);}
void debut() {courant=tete;}
void avance() {if (courant!=null) {courant=courant.suivant;}}
boolean estEnFin() {return courant==null;}
Personne eltCourant() { if (courant!=null) {return courant.pers;}
else {return null;}
}
}
class Personne {
String nom; final Personne pere; final Personne mere;
// liste des enfants de cette personne
ListePersonnes enfants=new ListePersonnes();
Personne(String n, Personne p, Personne m) {
nom=n; pere=p; mere=m;
// ajout de cette personne, lors de sa creation, a la liste
// des enfants de son pere et de sa mere (s'ils existent)
if (pere!=null) {pere.enfants.ajoute(this);};
if (mere!=null) {mere.enfants.ajoute(this);};
}
}
class TestGenealogie {
public static void main(String argv[]) {
// creation de la population
Personne adam= new Personne("adam",null,null);
Personne eve = new Personne("eve", null,null);
Personne abel= new Personne("abel",adam,eve);
Personne cain= new Personne("cain",adam,eve);
Personne ida = new Personne ("ida",cain,eve);
// recherche des enfants de la grand-mere paternelle de Ida
ListePersonnes resultat = ida.pere.mere.enfants;
resultat.debut(); System.out.println();
while (!resultat.estEnFin()) {
System.out.println(resultat.eltCourant().nom); resultat.avance();
};
}
}
99
Corrig 5
class Document{
String titre;
Document(String t) {titre=t;}
}
class Livre extends Document{
String auteur;
int nbreDePages;
Livre(String t,String aut, int nbp) {
super(t); auteur=aut; nbreDePages=nbp;
}
}
class Dictionnaire extends Document{
int nbreDeDefinitions;
Dictionnaire(String t,int nbDef) {
super(t); nbreDeDefinitions=nbDef;
}
}
class TestBibli {
public static void main(String argv[]) {
Livre pereGoriot= new Livre("Le pere Goriot","Balzac", 458);
Document doc; Livre livre;
doc = pereGoriot;
System.out.println(doc.titre);
System.out.println(doc.auteur);
livre = doc;
}
}
Erreurs la compilation :
> javac TestBibli.java
TestBibli.java:24: No variable auteur defined in class Document.
System.out.println(doc.auteur);
^
TestBibli.java:25: Incompatible type for =.
Explicit cast needed to convert Document to Livre.
livre = doc;
100
class Document{
String titre;
Document(String t) {titre=t;}
}
class TestTestDeClasse {
public static void main(String argv[]) {
ListDocuments bibli= new ListDocuments();
bibli.ajoute(
bibli.ajoute(
bibli.ajoute(
bibli.ajoute(
bibli.ajoute(
new
new
new
new
new
101
Corrig 6
imprime
A
102
Corrig 7
import es.*;
abstract
abstract
abstract
abstract
abstract
103
class TestListeOrdonnee {
public static void main(String argv[]) {
ListeOrdonnee l1= new ListeOrdonneeTable(40);
ListeOrdonnee l2= new ListeOrdonneeChainee();
System.out.print("\nl1 = "); l1.saisir();
System.out.print("\nl2 = "); l2.saisir();
System.out.print("\nl1 = "); l1.editer();
System.out.print("\nl2 = "); l2.editer();
l1.fusion(l2); System.out.print("\nl1 = l1+l2 :
l2.fusion(l1); System.out.print("\nl2 = l2+l1 :
l1.fusion(l1); System.out.print("\nl1 = l1+l1 :
l2.fusion(l2); System.out.print("\nl2 = l2+l2 :
}
}
104
");
");
");
");
l1.editer();
l2.editer();
l1.editer();
l2.editer();
Corrig 8
Interfaces - polymorphisme
interface Compare {
public boolean inf(Object x, Object y);
}
class AlgoGene {
public static void tri (Object [] T, Compare op) {
for (int i=T.length-1; i>=0; i--) {
for (int j=1; j<=i; j++) {
if (op.inf(T[j],T[j-1])) {
Object x=T[j-1]; T[j-1]=T[j]; T[j]=x;
}}}
}}
class Personne {
public String nom; public int age; public int poids;
public Personne(String n, int a, int p){
nom=n; age=a; poids=p;
}}
class ComparAge implements Compare {
public boolean inf(Object x, Object y) {
return ((Personne) x).age < ((Personne) y).age;
}}
class ComparPoids implements Compare {
public boolean inf(Object x, Object y) {
return ((Personne) x).poids < ((Personne) y).poids;
}}
class TestTriGen {
static public void main (String [] argv) {
Personne [] peuple = new Personne[4];
peuple[0] = new Personne("toto", 25, 80);
peuple[1] = new Personne("tutu", 53, 65);
peuple[2] = new Personne("tata", 15, 47);
peuple[3] = new Personne("jojo", 12, 30);
AlgoGene.tri(peuple, new ComparAge());
System.out.println("\nTRI SELON AGE");
for (int i=0; i<4; i++) {
System.out.println(peuple[i].nom);
};
AlgoGene.tri(peuple, new ComparPoids());
System.out.println("\nTRI SELON POIDS");
for (int i=0; i<4; i++) {
System.out.println(peuple[i].nom);
};
}}
105
Corrig 9
Interfaces - polymorphisme
import es.*;
interface Ope {
public Object neutre();
public Object mul(Object x, Object y);
public Object add(Object x, Object y);
public Object saisir();
public void imprimer(Object x);
}
class Matrice {
public Object [][] M;
public Matrice(int n) {M = new Object [n][n];}
public static Matrice produit (Matrice A, Matrice B, Ope op) {
Matrice C =new Matrice(A.M.length);
for (int i=0; i<A.M.length; i++) {
for (int j=0; j<A.M.length; j++) {
C.M[i][j] = op.neutre();
for (int k=0; k<A.M.length; k++) {
C.M[i][j] =
op.add(C.M[i][j], op.mul(A.M[i][k],B.M[k][j]));
}}}
return C;
}
public static Matrice somme (Matrice A, Matrice B, Ope op) {
Matrice C =new Matrice(A.M.length);
for (int i=0; i<A.M.length; i++) {
for (int j=0; j<A.M.length; j++) {
C.M[i][j] = op.add(A.M[i][j],B.M[i][j]);
}}
return C;
}
public void saisir (Ope op) {
for (int i=0; i<M.length; i++) {
for (int j=0; j<M.length; j++) {
M[i][j] = op.saisir();
}}
}
public void imprimer(Ope op) {
for (int i=0; i<M.length; i++) {
System.out.println("");
for (int j=0; j<M.length; j++) {
System.out.print(" "); op.imprimer(M[i][j]);
};
}
System.out.println("");
}}
class Int {
public final int val;
public Int(int v) {val=v;}
}
106
107
System.out.println("\n\ntest graphe");
Ope warshall = new OpeWarshall();
Matrice G = new Matrice(5);
System.out.println("\nMatrice G 5x5 :"); G.saisir(warshall);
Matrice PCC= G;
for (int i=0; i<4; i++) {
PCC =
Matrice.somme(Matrice.produit(PCC,G,warshall),G, warshall);
};
System.out.println("\nPCC :");
PCC.imprimer(warshall);
}}
exemple de donne
matrices numriques :
1 2 3
4 5 6
7 8 9
2 0 0
0 2 0
0 0 2
graphe :
0
5 999 999 3
999 0
3 999 999
999 999 0
4 999
999 999 999 0
2
999 1
2 999 0
*/
108
Corrig 10
Processus - moniteurs
class MoniteurProdCons {
String tampon; boolean estVide=true;
n) { tampon=t; nom=n;}
e)
e)
e)
e)
{};
{};
{};
{};
tampon.cons(nom);
tampon.cons(nom);
tampon.cons(nom);
tampon.cons(nom);
rsultat du programme :
p1 PRODUIT
: message1
PRODUCTEUR p1 ATTEND
PRODUCTEUR p2 ATTEND
c1 CONSOMME : message1
p1 PRODUIT
: message2
p2 PRODUIT
: message1
PRODUCTEUR p2 ATTEND
c1 CONSOMME : message1
p2 PRODUIT
: message2
c1 CONSOMME : message2
CONSOMMATEUR c1 ATTEND
de p1
de p1
de p1
de p2
de p2
de p2
de p2
blocage ...
Programmation par objets en Java
109
class MoniteurProdCons {
// Version correcte
String tampon; boolean estVide=true; // construite selon le principe
// general preconise
synchronized void prod(String m,String nom) {
while (!estVide){ System.out.println("PRODUCTEUR "+nom+" ATTEND");
try {wait();} catch(InterruptedException e) {};
};
System.out.println(nom+" PRODUIT
: " + m);
tampon=m; estVide=false; notifyAll();
}
synchronized String cons(String nom) {
while (estVide){ System.out.println("CONSOMMATEUR "+nom+" ATTEND");
try {wait();} catch(InterruptedException e) {};
};
System.out.println(nom+" CONSOMME : " + tampon);
String resul=tampon; estVide=true; notifyAll(); return resul;
}}
n) { tampon=t; nom=n;}
e)
e)
e)
e)
{};
{};
{};
{};
tampon.cons(nom);
tampon.cons(nom);
tampon.cons(nom);
tampon.cons(nom);
rsultat du programme :
p1 PRODUIT
: message1
PRODUCTEUR p1 ATTEND
PRODUCTEUR p2 ATTEND
c1 CONSOMME : message1
p1 PRODUIT
: message2
PRODUCTEUR p2 ATTEND
c1 CONSOMME : message2
p2 PRODUIT
: message1
PRODUCTEUR p2 ATTEND
c1 CONSOMME : message1
p2 PRODUIT
: message2
c1 CONSOMME : message2
110
de p1
de p1
de p1
de p1
de p2
de p2
de p2
de p2
Corrig 11
Applets
Document HTML :
<HTML>
<HEAD> <TITLE> TEST APPLET 1</TITLE> </HEAD>
<BODY>
<P>
AU DESSUS DE L'APPLET
</P>
<APPLET CODE= Boing2.class WIDTH=250 HEIGHT=150>
</APPLET>
<P>
AU DESSOUS DE L'APPLET
</P>
</BODY>
</HTML>
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999
111
import java.applet.*;
import java.awt.*;
112
Corrig 12
113
Corrig 13
Voir corrig du TP6
Corrig 14
Voir corrig du TP7
114
Corrig TP1
import es.*;
class Etudiant {
public String nom; public String adresse;
public Etudiant() {
System.out.print("\nnom: "); nom=Lecture.chaine();
System.out.println();
System.out.print("adresse: "); adresse=Lecture.chaine();
System.out.println();
}
public void changeAdresse() {
System.out.print("\nnouvelle adresse: ");
adresse=Lecture.chaine();
}}
class ListEtudiants {
private int N;
private Etudiant T[]= new Etudiant[100];
public ListEtudiants() {N=0;};
public void ajouter() {
if (N<100){ T[N]=new Etudiant(); N=N+1;};
}
public Etudiant chercher(String nom) {
int i=0;
while (i<N) {if ((T[i].nom).equals(nom)) {return T[i];}; i++;};
return(null);
}}
class Matiere{
private int N;
private Etudiant T[]= new Etudiant[100]; private int note[]= new int[100];
public Matiere() {N=0;}
public void ajouter(Etudiant etu, int n) {
if (N<100) {T[N]=etu; note[N]=n; N=N+1;};
}
public int consulteNote(Etudiant etu) {
int i=0;
while (i<N) {if (T[i]==etu) {return note[i];}; i=i+1;};
return(99);
}
public void imprimer() {
for(int i=0; i<N; i++) {
System.out.print("\n nom: "+T[i].nom+" adresse: "+T[i].adresse);
System.out.print("
note: "+note[i]);
}
}
public float moyenne() {
int somme=0;
for(int i=0; i<N; i++) { somme=somme+note[i];};
if (N==0) {return 99;}
else {return(((float) somme)/N);};
}
}
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999
115
class TestEtudiants {
static void afficheMenu() {
System.out.print( "\ne: ajout etudiant a: modif adresse"
+ " r: recherche etudiant"
+ "\nn: ajout de note
l: liste des notes et moyenne"
+ "\nc: consultation de note
q: quitter\n");
}
public static void main(String argv[]) {
ListEtudiants lesEtudiants=new ListEtudiants();
Matiere francais=new Matiere(), anglais=new Matiere(), maths=new Matiere();
char cmd;
afficheMenu();
while ((cmd=Lecture.unCarCmde())!='q') {
String nom; Etudiant e; int note;
char cmat;
switch (cmd) {
case 'e' : // ajout d'un etudiant
lesEtudiants.ajouter(); break;
case 'a' : // modification d'adresse
System.out.print(" nom etudiant : "); nom=Lecture.chaine();
e = lesEtudiants.chercher(nom);
if (e!=null) {e.changeAdresse();}
else {System.out.print("\nEtudiant inconnu\n");}; break;
case 'r' : // recherche d'un etudiant
System.out.print(" nom etudiant : "); nom=Lecture.chaine();
e = lesEtudiants.chercher(nom);
if (e!=null) {System.out.print(" \nadresse : " + e.adresse);}
else {System.out.print("\nEtudiant inconnu\n");}; break;
case 'n' : // ajout d'une note
System.out.print(" matiere : "); cmat=Lecture.unCarCmde();
Matiere mat;
switch (cmat) {
case 'a' : mat=anglais; break;
case 'f' : mat=francais; break;
case 'm' : mat=maths; break;
default : mat=null; break;
};
if (mat==null)
{System.out.print("\nmatiere inconnue\n"); break;};
System.out.print(" etudiant : ");
nom=Lecture.chaine(" ,.\n");
e = lesEtudiants.chercher(nom);
if (e!=null) {
System.out.print(" \nnote : "); note=Lecture.unEntier();
mat.ajouter(e,note); System.out.println();
}
else {System.out.print("\nEtudiant inconnu\n");}; break;
case 'l' : // liste des notes et moyenne
System.out.print(" matiere : ");
cmat=Lecture.unCarCmde();
switch (cmat) {
case 'a' : mat=anglais; break;
case 'f' : mat=francais; break;
case 'm' : mat=maths; break;
default : mat=null; break;
};
116
if (mat==null)
{System.out.print("\nmatiere inconnue\n"); break;};
System.out.print(" liste des notes : ");
mat.imprimer();
System.out.println("\nmoyenne : " + mat.moyenne()); break;
case 'c' : // consultation de note
System.out.print(" nom etudiant : "); nom=Lecture.chaine();
e = lesEtudiants.chercher(nom);
if (e==null) {System.out.print("\nEtudiant inconnu\n"); break;}
System.out.print(" \nmatiere : ");
cmat=Lecture.unCarCmde();
switch (cmat) {
case 'a' : mat=anglais; break;
case 'f' : mat=francais; break;
case 'm' : mat=maths; break;
default : mat=null; break;
};
if (mat==null)
{System.out.print("\nmatiere inconnue\n"); break;};
note= mat.consulteNote(e);
System.out.println("
note= " + note); break;
};
afficheMenu();
}
}
}
117
Corrig TP2
import es.*;
abstract class Matrice {
public static final int N=3;
public void add(Matrice M) {
for (int i=0; i<N; i=i+1) {
for (int j=0; j<N; j=j+1) { affElt(i, j, elt(i,j)+M.elt(i,j));};
};
}
public void mul(Matrice M) {
Matrice resul=tempo();
for (int i=0; i<N; i=i+1) {
for (int j=0; j<N; j=j+1) {
float s=0;
for (int k=0; k<N; k=k+1) {s=s+elt(i,k)*M.elt(k,j);}
resul.affElt(i,j,s);
};
};
affecter(resul); // this recoit resul
}
public abstract float elt(int i, int j);
public abstract void affElt(int i, int j, float v);
public abstract Matrice tempo();
public void saisir() {
for (int i=0; i<N; i=i+1) {
for (int j=0; j<N; j=j+1) {float v= Lecture.unReel(); affElt(i, j, v);};
};
}
public void imprimer() {
for (int i=0; i<N; i=i+1) {
System.out.println("");
for (int j=0; j<N; j=j+1) {System.out.print("
};
System.out.println("");
}
"+ elt(i,j));};
class TestMatrices {
public static void main(String[] argv) {
Matrice M1 = new MatriceTab(); Matrice M2 = new MatriceList();
Matrice M3 = new MatriceTab(); Matrice M4 = new MatriceList();
System.out.println("\nMatrice M1 :"); M1.saisir(); M3.affecter(M1);
System.out.println("\nMatrice M2 :"); M2.saisir(); M4.affecter(M2);
M1.mul(M2); System.out.println("\nProduit M1xM2 :"); M1.imprimer();
M4.mul(M3); System.out.println("\nProduit M4xM3 :"); M4.imprimer();
}
}
119
Corrig TP3
class Fil {
private char EtatCourant,EtatFutur;
public Fil() {EtatCourant='X'; tous.ajoutEnFin(this);};
public char lire() { return EtatCourant; };
public void ecrire(char val) { EtatFutur=val;};
public void actualise() { EtatCourant=EtatFutur;};
public static ListPrcr /*de Fil*/ tous=new ListPrcr();
};
120
class NON_ET {
private ET n; private INV i; private Fil f=new Fil();
public NON_ET(Fil e1, Fil e2, Fil s) {n= new ET(e1,e2,f); i= new INV(f,s);};
};
class SimulVerrou {
private Fil ma0=new Fil();
private Fil ma1=new Fil();
private Fil s=new Fil();
private VERROU v=new VERROU(ma0,ma1,s);
private FournisseurDeBit ema0;
private FournisseurDeBit ema1;
private AfficheurDeBit affma0;
private AfficheurDeBit affma1;
private AfficheurDeBit affs;
public SimulVerrou(FournisseurDeBit pema0, FournisseurDeBit pema1,
AfficheurDeBit paffma0, AfficheurDeBit paffma1, AfficheurDeBit paffs){
ema0=pema0; ema1=pema1; affma0=paffma0; affma1=paffma1; affs=paffs;
}
public void top() {
ma0.ecrire(ema0.lire());
ma1.ecrire(ema1.lire());
CircuitDeBase.tous.debut();
while (!CircuitDeBase.tous.estEnFin()) {
((CircuitDeBase) CircuitDeBase.tous.eltCourant()).topEval();
CircuitDeBase.tous.avance();
};
Fil.tous.debut();
while (!Fil.tous.estEnFin()) {
((Fil) Fil.tous.eltCourant()).actualise();
Fil.tous.avance();
};
affma0.ecrire(ma0.lire()); affma1.ecrire(ma1.lire());
affs.ecrire(s.lire());
}
}
interface FournisseurDeBit {
public char lire();
}
interface AfficheurDeBit {
public void ecrire(char b);
}
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999
121
Placement.p(this,poussoirMa0,1,1,1,1);
Placement.p(this,poussoirMa1,1,2,1,1);
Placement.p(this,boutonTop, 1,3,1,1);
Placement.p(this,boutonQuit, 1,4,1,1);
Placement.p(this,affichageMa0, 2,1,1,1);
Placement.p(this,affichageMa1, 2,2,1,1);
Placement.p(this,affichageS , 2,3,1,1);
boutonTop.addActionListener(new ActionTop());
boutonQuit.addActionListener(new ActionQuit());
pack(); setVisible(true);
simulateur = new SimulVerrou(new Ma0(), new Ma1(),
new AfficheMa0(), new AfficheMa1(), new AfficheS());
}}
public class tp3_simu {
static public void main(String argv[]) {new FenetreSimul();}
}
122
Corrig TP4
Trieuse parallle
import es.*;
class Tamp1 { // modele de moniteur tampon a une place
int tampon; boolean estVide=true; String nom;
synchronized void prod(int v) {
if(!estVide){try {wait();} catch(InterruptedException e){};};
tampon=v; estVide=false; notify();
}
synchronized int cons() {
if(estVide){try {wait();} catch(InterruptedException e){};};
int resul=tampon; estVide=true; notify(); return resul;
}}
class Trieuse {
static final int infini = 99999999;
static final int N = 5; // nombre d'emplacements
class Emplacement extends Thread { // modele de processus emplacement
int min; int max;
Tamp1 precedentE; Tamp1 suivantS; Tamp1 precedentS; Tamp1 suivantE;
public Emplacement(Tamp1 tprecE, Tamp1 tprecS, Tamp1 tsuivS, Tamp1 tsuivE) {
precedentE=tprecE; precedentS=tprecS; suivantS=tsuivS; suivantE=tsuivE;
}
public void run() {
min=infini; max=infini;
for(int i=0; i<2*N; i++) {
suivantE.prod(max); max=precedentE.cons();
if(max<min) {int v=max; max=min; min=v;};
};
for(int i=0; i<2*N; i++) {
precedentS.prod(min); min=suivantS.cons();
if(max<min) {int v=max; max=min; min=v;};
};
}}
public Trieuse() {
Tamp1 tPrecE=tamponE; Tamp1 tPrecS=tamponS;
for(int i=0; i<N-1; i++) { // cree N emplacements interconnectes par tampons
Tamp1 tSuivS= new Tamp1(); Tamp1 tSuivE= new Tamp1();
new Emplacement(tPrecE,tPrecS,tSuivS,tSuivE).start();
tPrecE=tSuivE; tPrecS=tSuivS;
};
new Emplacement(tPrecE,tPrecS,bouchon,bouchon).start();
123
class Trieuse {
static final int infini = 9999;
static final int N = 5; // nombre d'emplacements
class BiAfficheur extends Panel {
Label l1; TextField t1=new TextField(4);
Label l2; TextField t2=new TextField(4);
public BiAfficheur(String s1, String s2) {
l1=new Label(s1); l2= new Label(s2);
Placement.p(this,l1,1,1,1,1); Placement.p(this,t1,2,1,1,1);
Placement.p(this,l2,1,2,1,1); Placement.p(this,t2,2,2,1,1);
t1.setEditable(false); t1.setBackground(Color.white);
t2.setEditable(false); t2.setBackground(Color.white);
}
public void afficher(int v1, int v2, int duree) {
t1.setText(interpEnt(v1)); t2.setText(interpEnt(v2));
try {Thread.sleep(duree); } catch(InterruptedException e){};
}
String interpEnt(int v) {
if (v==infini) return ""; else return String.valueOf(v);
}
}
class Emplacement extends Thread { // modele de processus emplacement
BiAfficheur minmax=new BiAfficheur("min","max");
int min; int max;
Tamp1 precedentE; Tamp1 suivantS; Tamp1 precedentS; Tamp1 suivantE;
public Emplacement(Tamp1 tprecE, Tamp1 tprecS, Tamp1 tsuivS, Tamp1 tsuivE) {
precedentE=tprecE; precedentS=tprecS; suivantS=tsuivS; suivantE=tsuivE;
}
124
minmax.afficher(min,max,500);
minmax.afficher(min,max,1000);
minmax.afficher(min,max,500);
minmax.afficher(min,max,1000);
public Trieuse() {
TextField txtResul= new TextField(25);
Placement.p(fTrieuse,txtResul,0,3,1,1);
Tamp1 tPrecE=tamponE; Tamp1 tPrecS=tamponS;
for(int i=1; i<N; i++) { // cree N emplacements interconnectes par tampons
Tamp1 tSuivS= new Tamp1(); Tamp1 tSuivE= new Tamp1();
Emplacement emp=new Emplacement(tPrecE,tPrecS,tSuivS,tSuivE);
Placement.p(fTrieuse,emp.minmax,i,1,1,1); // positionne l'afficheur
emp.start();
tPrecE=tSuivE; tPrecS=tSuivS;
};
Emplacement emp= new Emplacement(tPrecE,tPrecS,bouchon,bouchon);
Placement.p(fTrieuse,emp.minmax,N,1,1,1);
emp.start();
fTrieuse.pack(); fTrieuse.setVisible(true);
try {Thread.sleep(5000); } catch(InterruptedException e){};
System.out.println("entrer "+2*N+" valeurs entieres");
for(int i=0; i<2*N; i++) { tamponE.prod(Lecture.unEntier());};
for(int i=0; i<2*N; i++) {
txtResul.setText(txtResul.getText()+tamponS.cons()+" ");
};
System.out.println("termine"); Lecture.unCarCmde();
System.exit(0);
}}
125
Corrig TP5
= 1;
= 2;
= 3;
127
Corrig TP6
Socket - ServerSocket
129
Corrig TP7
RMI
extends UnicastRemoteObject
implements Repondeur {
Frame f ;
TextField question = new TextField(40);
TextField reponse = new TextField(40);
Label lq=new Label("Question : ");
Label lr=new Label("Reponse : ");
Button boutonRepond = new Button("REPOND");
class ActionRepond implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
ImplementRepondeur.this.reveille();
}
}
public ImplementRepondeur() throws RemoteException {
f=new Frame("SERVICE");
Placement.p(f,lq,0,1,1,1); Placement.p(f,question,1,1,1,1);
Placement.p(f,boutonRepond,1,2,1,1);
Placement.p(f,lr,0,3,1,1); Placement.p(f,reponse,1,3,1,1);
boutonRepond.addActionListener(new ActionRepond());
f.pack(); f.setVisible(true);
}
public void poseQuestion(String q) { question.setText(q);}
public void fin() {f.dispose();}
public synchronized String obtientReponse () {
try {wait();} catch(Exception e) {};
return(reponse.getText());
}
public synchronized void reveille() {notify();}
}
131
132
<HTML>
<HEAD> <TITLE>Test de ClientRmi</TITLE> </HEAD>
<BODY LINK="#0000ff" VLINK="#800080" BGCOLOR="#ffffff">
<FONT SIZE=6><P>Test de ClientRmi</P></FONT>
<P>
BLABLABLA
</P>
<HR>
<applet code=T2AppletQuestionneur.class width=600 height=200>
</applet>
<P> BLABLABLA </P>
</HTML>
Programmation par objets en Java
133
Listes : list
package list;
// Listes dotes d'un etat de parcours
public class ListPrcr {
protected class Maillon extends Object {
public Object elt; public Maillon suivt;
public Maillon(Object e, Maillon s) {elt=e; suivt=s;}
};
protected Maillon premier;
protected Maillon dernier;
protected Maillon courant;
// constructeur : initialise a vide, parcours en fin.
public ListPrcr() { premier= null; courant= null; }
// ajoute elt en fin de liste
public void ajoutEnFin(Object elt) {
Maillon nouveau = new Maillon(elt,null);
if (premier==null) { premier= nouveau; }
else { dernier.suivt= nouveau; };
dernier= nouveau;
}
// positionne le parcours en debut de liste
public void debut() { courant= premier; }
// fait avancer le parcours d'une position,
// ne fait rien si le parcours est en fin de liste
public void avance() {if (courant!=null) {courant= courant.suivt;};}
// supprime l'element courant du parcours et avance,
// ne fait rien si le parcours est en fin de liste
public void retire() {
Maillon prec;
if (courant!=null) {
if (courant==premier) { /* cas particulier */ premier= courant.suivt; }
else { // cas general : recherche du precedent de courant
prec= premier;
while (prec.suivt != courant) { prec= prec.suivt; };
prec.suivt= courant.suivt; // decrochage de courant
if (dernier==courant) {/* situation particuliere */ dernier= prec;};
};
}; courant= courant.suivt;
}
// element courant du parcours, rend null si le parcours est en fin de liste
public Object eltCourant() {
if (courant!=null) {return courant.elt;} else {return null;};
}
// indique que la liste est vide
public boolean estVide() { return (premier==null); }
// indique que le parcours est en fin de liste (au dela du dernier)
public boolean estEnFin() { return (courant==null); }
}
135
136