Vous êtes sur la page 1sur 140

PROGRAMMATION PAR OBJETS EN

Java

Pascale LE CERTEN
lecerten@irisa.fr
Lucien UNGARO
ungaro@irisa.fr
mars 1999

IFSIC - Campus de Beaulieu - 35042 Rennes Cedex


Tel. 02 99 84 71 00 - fax 02 99 84 71 71

Table des matires


1

Classes et objets
1.1
1.2
1.3
1.4
1.5

1.6
1.7
1.8
2

Structure des fichiers sources


Paquetages : package

10
11

Types et structures de contrle


3.1

3.2
3.3
3.4
3.5

3.6
3.7
4

1
2
4
4
5
5
5
6
6
8

Structure et environnement des programmes


2.1
2.2

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

Usage simple de lhritage : ajout de proprits


Accessibilit : public, protected, private
Compatibilit entre types
Mthodes virtuelles
Dfinition de mthodes virtuelles
Liaison dynamique des mthodes virtuelles
Quelques utilisations des mthodes virtuelles

Gnricit - Ralisation imparfaite en Java

Interfaces - polymorphisme

Interfaces utilisateurs : paquetage AWT

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

Communications rseau : net


Adresses Internet
Communications de bas niveau
Communications fiables en mode connect
Accs par URL

70
70
73
77

Applications rparties : rmi


11.1 Mcanisme illustr sur un exemple
11.2 Communication de rfrences dobjets distants
11.3 Passage des paramtres et rendu de rsultat

12

48
49
49
51
52
52
52
54
55
55
56

Documents HTML actifs : Applet

10.1
10.2
10.3
10.4
11

41
43
44
45

Paralllisme en Java : Threads


8.1
8.2

Organisation gnrale des interfaces utilisateurs


Gestion des vnements
Placement des composants
Quelques mthodes des classes de AWT

78
81
83

Sujets de travaux pratiques


TP1
TP2
TP3
TP4
TP5
TP6
TP7

Gestion des tudiants


Deux reprsentations des matrices
Simulateur de circuits logiques
Tri parallle
Applet
Communications rseau
Application rpartie - RMI

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES


Paquetages utiliss dans les exercices
Ce cours est disponible sur le serveur pdagogique de SPECIF :
http://www.ove.fr/cgi-bin/w3-msql/spd/cours.html
Ouvrage de rfrence :
Thinking in Java - Bruce Eckel
http://www.EckelObjects.com/javabook.html

85
86
87
90
92
93
94
95
134

Programmation par objets


en Java
1

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

tournerADroite

Classes et objets

Il existe de nombreux langages permettant la programmation par objets : Simula,


Smalltalk, Eiffel, Turbo Pascal Objet, C++, Java. Ces langages ont en commun les
notions suivantes :
La notion dobjet, qui possde deux aspects totalement indpendants :
- Un objet est une chose dote dune identit (on peut le rfrencer). Ceci

soppose la notion de valeur (par exemple 12 nest pas un objet).


- Un objet peut, accessoirement, tre compos de donnes et de procdures
lies ces donnes : les donnes de lobjet constituent le contexte de travail
pour les procdures associes.
La notion de classe, qui nest autre que celle de type (ou modle) dobjets,
La notion dhritage : lhritage permet de dfinir des types dobjets plus prcis

partir de types dobjets plus gnraux.


Si la programmation par objets se limitait la notion dobjet et de classe, elle ne serait
quun simple conseil mthodologique et ne ncessiterait pas vraiment de langage particulier. Il est toujours possible en effet, dans un langage tel que Pascal ou C, de
regrouper les textes de dfinition des types de donnes (structures) et des procdures
qui accdent aux donnes de ces types.
En revanche, la notion dhritage est plus profonde et ne peut tre ralise simplement
par discipline, elle doit tre offerte par le langage. Elle permet de dfinir une hirarchie de types, des plus gnraux aux plus spcialiss. Lhritage et les notions qui lui
sont associes, notamment les procdures virtuelles, permettent la conception de logiciels extensibles et favorisent la rutilisation de modules logiciels existants.
Dans les paragraphes qui suivent, nous prsentons les notions de programmation par
objets du langage Java.

1.2 Dfinition dune classe


La notion de classe est un enrichissement de la notion usuelle de type. Une classe permet de dfinir un type dobjet, ventuellement compliqu, associant des donnes et
des procdures qui accdent ces donnes. Les donnes se prsentent sous forme de
champs dsigns par identificateurs et dots dun type. Ces champs sont gnralement
des variables qui reprsentent ltat de lobjet.
Les procdures, galement appeles mthodes, dfinissent les oprations possibles sur
un tel objet. Ces donnes et procdures sont qualifies de membres ou composants de
la classe.
Nous prenons comme exemple de dclaration de classe, les robots prcdents.
2

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

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)

public Robot(int x, int y, int o) { (initialisation)


X=x; Y=y; orientation=o;
}
public Robot() {
(autre initialisation possible)
X=0; Y=0; orientation=Nord;
}
public void avancer() { switch (orientation) {
case Nord : Y=Y+1; break;
case Est : X=X+1; break;
case Sud : Y=Y-1; break;
case Ouest: X=X-1; break;
};
}
public void tournerADroite(){
switch (orientation) {
case Nord : orientation=Est ;
case Est : orientation=Sud ;
case Sud : orientation=Ouest;
case Ouest: orientation=Nord ;
};
}
}

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

1.3 Dclaration et cration dobjets


Une dfinition de classe constitue un modle dobjet dont on peut ensuite crer autant
dexemplaires que lon veut (on dit galement instances). En Java, la dclaration et la
cration des objets sont deux choses spares. La dclaration consiste dfinir un
identificateur et lui associer un type afin que cet identificateur puisse dsigner un objet
de ce type. Par exemple, la dclaration des identificateurs totor et vigor pour
dsigner des objets de type Robot scrit :
Robot totor; Robot vigor;
En Java, comme dans de nombreux langages objets tels que Eiffel, Pascal-Delphi ou
Smalltalk, un identificateur de variable de type classe est associ une rfrence qui
dsigne un objet. Une rfrence est ladresse dun objet, mais contrairement un
pointeur (tel quon en trouve en C, C++ ou Pascal) la valeur dune rfrence nest ni
accessible, ni manipulable : elle ne peut que permettre laccs lobjet quelle dsigne.
La dclaration seule ne cre pas dobjet. Elle associe lidentificateur une rfrence
appele null qui ne fait rfrence rien.

totor

null

vigor

null

Pour crer un objet de classe C, il faut excuter new en citant un constructeur de la


classe C, avec ses paramtres. La primitive new rend en rsultat la rfrence sur
lobjet cr, quil suffit alors daffecter la variable dclare :
...; totor = new Robot(0,0,Nord);
...; vigor = new Robot(5,12,Sud);
totor

objet Robot

Il est possible de mler dclaration et cration :


Robot totor = new Robot(0,0,Nord);
Robot vigor = new Robot(5,12,Sud);

1.4 Utilisation des objets


Les composants dun objet (champs de donnes et mthodes) sont dsigns au moyen
dune notation pointe :
totor.X;
abscisse de totor
vigor.avancer();
fait avancer vigor
totor.tournerADroite(); fait tourner totor

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Classes et objets

1.5 Rdaction des mthodes


1.5.1 Dclaration de mthode
En Java, les textes des mthodes sont rdigs lintrieur de la dfinition dune classe.
Une dclaration de mthode a la forme suivante :
T PPP (T1 p1, T2 p2, ...) {... Corps ...}
T est le type du rsultat (void si la mthode ne rend pas de rsultat).
PPP est le nom de la mthode.
T1 p1, T2 p2, ... est la liste des paramtres avec leur type. Si une mthode
na pas de paramtre, il faut tout de mme conserver les parenthses : T PPP().
Corps est compos de dclarations de variables et dinstructions. Les dclarations
peuvent tre mles aux instructions.
Le rendu de rsultat se note return expression :
boolean superieur (float x, float y) {return x>y;}
On peut donner le mme nom plusieurs mthodes, condition quelles diffrent par
le nombre ou le type des paramtres.
1.5.2 Instance courante : this
Dans le cas o un appel de mthode porte sur un objet, cet objet est appel communment linstance courante. Au sein des mthodes, les composants de linstance courante sont directement dsignables par leur identificateur.
Par exemple, lors de lexcution de vigor.avancer(), X et Y dsignent les composants X et Y de vigor.
Citation explicite de linstance courante : this
Dans certains cas, on peut avoir besoin de citer explicitement linstance courante dans
le texte dune mthode, par exemple pour passer cette instance en paramtre de procdure. On dispose pour cela du mot cl this (littralement lui).
Prenons un exemple classique, le placement de boutons dans une fentre de dialogue.
La procdure de placement peut tre dfinie dans une classe part, Placement.
class Placement {
...
public static void placer(Fenetre f,Bouton b,int X,int Y)
{...}
}
La classe Fenetre ci-dessous place un bouton au sein delle-mme en se passant en
paramtre la mthode placer() :
class Fenetre {
Bouton b=new Bouton();
...
Fenetre() { ... Placement.placer(this, b, 10, 30) ... }
...
}
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999

Classes et objets

1.6 Composants non lis des objets : static


On a parfois besoin de procdures non associes un objet. Une telle procdure doit
tre rdige dans une classe, car il ny a pas de procdures isoles en Java. Cela est
souvent naturel, car la classe joue alors un rle structurant en regroupant les procdures autour dun mme thme. Par exemple, on peut vouloir rdiger une procdure qui
compare deux objets de type Robot et rend le meilleur des deux, mais sans vouloir
associer la mthode leMeilleur lun plutt qu lautre. Pour cela, il faut dclarer la procdure avec lattribut static.
class Robot {
public int solidite;
...
public static Robot leMeilleur(Robot r1, Robot r2) {
if (r1.solidite > r2.solidite){return r1;}
else {return r2;};
}}
Pour utiliser une telle procdure, il faut prfixer son nom par celui de la classe :
Robot.leMeilleur(totor, vigor);
Une mthode static nayant pas dobjet courant, this ny est pas dfini.
De mme, on peut dfinir des donnes qui ne sont pas des composants dobjets. Ces
donnes sont dclares avec lattribut static, et chacune nexiste quen un seul
exemplaire pour toute la classe.
Comment dfinir une simple procdure en Java
Il ny a pas de procdures isoles en Java, il ny a que des mthodes de classe. Pour
avoir une procdure, on est donc oblig dinventer artificiellement une classe. On peut
adopter la convention suivante : donner un nom significatif la classe, et un nom muet
la mthode, par exemple _()ou p(). Ainsi pour rdiger une procdure qui teste si
un entier est pair :
class EstPair{
public static boolean p(int n) {return (n%2)==0;}
}
appel : EstPair.p(12);

1.7 Abstraction de la reprsentation : private et public


On utilise gnralement une classe pour raliser un type de donnes ou bien un comportement dobjet dont on souhaite cacher la mise en uvre lutilisateur, afin dassurer une meilleure fiabilit au logiciel : en effet, ceci interdit lutilisation abusive de
proprits qui nappartiennent pas vraiment au type de donnes, localise leffet des
erreurs et autorise le concepteur de la classe modifier sa mise en uvre, pour des raisons defficacit par exemple, sans rien changer au reste de lapplication.
Prenons lexemple simple du type de donnes pile dentiers. Ltat dun objet de ce
type est une suite finie, ventuellement vide, de valeurs entires qui ont t empiles
une une. Le sommet est la plus rcente des valeurs empiles, non encore retires. On
peut empiler une valeur. On peut galement dpiler, ce qui retire le sommet courant.
6

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

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

Ce type peut tre dfini au moyen dune classe PileEnt :


class PileEnt {
public int s;
public int P[] = new int[100]; // tableau de 100 lments
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];}
}
La classe ci-dessus laisse voir trop de choses lutilisateur. Le tableau P et lindex s
ne font pas partie des proprits dune pile, ce ne sont que des lments dune mcanique interne particulire. On pourrait raliser la pile autrement, par exemple au moyen
dune liste de maillons chans. Les programmes qui utilisent la classe PileEnt doivent tre indpendants de ces choix arbitraires de mise en uvre.

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 .

1.8 Classes internes


Java permet de dfinir une classe lintrieur dune autre classe. Une telle classe est
dite interne (inner class en anglais). Lintrt davoir une telle classe interne rside
dans la modularit que cela apporte : le nom de la classe interne est purement local et
cette classe peut mme tre rendue invisible depuis lextrieur de la classe englobante
si on la dclare avec lattribut private.
Par exemple, pour programmer une pile au moyen de maillons chans, on peut dfinir
une classe Maillon interne la classe Pile.
class PileEnt {
private class Maillon {
int elt; Maillon suivant;
Maillon(int e, Maillon s) {elt=e; suivant=s;}
}
private Maillon sommet;
public PileEnt() {sommet=null;}
public void empiler(int e) {
sommet=new Maillon(e,sommet);
}
public void depiler() {sommet=sommet.suivant;}
public int sommet() {return sommet.elt;}
}
lextrieur de la classe englobante, on peut citer une classe interne (non prive) sous
la forme : classe englobante.classe interne
Dans le cas simple ci-dessus, la classe interne ne fait rfrence aucun membre de la
classe englobante : on pourrait dans ce cas la dclarer avec lattribut static.
Dans le cas gnral, une classe interne utilise les composants de lobjet courant de la
classe englobante : il y a alors pratiquement une nouvelle classe interne par objet de la
classe englobante, chacune tant lie un objet. La cration dun objet de la classe
interne doit dans ce cas citer un objet de la classe englobante.
Voici un exemple, simple mais illustratif. Nous voulons doter la pile dun moyen de
parcours de ses lments, de telle sorte que plusieurs parcours puissent se drouler en
mme temps sur une mme pile. Une faon lgante de rsoudre ce problme consiste
dfinir une classe Parcours associe la pile et de crer un objet de cette classe
chaque fois que lon veut raliser un parcours. Le constructeur Parcours() initia8

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Structure et environnement des programmes

Structure et environnement des programmes

2.1 Structure des fichiers sources


Un programme Java consiste en une collection de dfinitions de classes. Un fichier
source (suffixe .java) peut contenir plusieurs dfinitions de classes et les classes
dune mme application peuvent tre dfinies dans des fichiers sources diffrents.
La compilation produit un fichier objet par classe (suffixe .class). Le compilateur
se lance par la commande :
javac fichier.java
Les fichiers objets .class ne sont pas des programmes pour une machine particulire : ils sont cods en un langage destin tre interprt. Ceci facilite la portabilit,
notamment par transmission travers les rseaux, de programmes Java dans des documents HTML.
Il ny a pas ddition de lien au sens habituel. Pendant lexcution, les fichiers
.class sont recherchs et chargs depuis le systme de fichiers selon certaines
rgles de recherche. Une des classes (TestFlipFlop dans lexemple ci-dessous)
constitue le dmarrage de lapplication. Cette classe doit dfinir une mthode
main() qui est appele au lancement, par la commande : java TestFlipFlop
FlipFlop.java
class FlipFlop {
String etat;
FlipFlop() {etat="flip";}
void change() {
if (etat.equals("flip")) {etat="flop";}
else {etat="flip";}
}
}
class TestFlipFlop {
static FlipFlop ff = new FlipFlop();
public static void main(String argv[]) {
Compteur cpt = new Compteur();
while (!cpt.egal(4)) {
System.out.println(ff.etat);
ff.change(); cpt.incr();
}
}
}
Compteur.java
class Compteur {
int i;
Compteur() {i=0;}
void incr() {i++;}
boolean egal(int j)
{return(i==j);}
}

javac FlipFlop.java

FlipFlop.class

TestFlipFlop.class

Compteur.class
javac Compteur.java

java TestFlipFlop

flip
flop
flip
flop

Le profil de main() est : public static void main(String argv[])


le paramtre argv est un tableau de chanes de caractres. Ce tableau est initialis
avec les chanes accompagnant (ventuellement) la commande de lancement.
10

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Structure et environnement des programmes

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

2.2 Paquetages : package


Les classes peuvent tre regroupes en paquetages. Les classes dun mme paquetage
sont disperses dans plusieurs fichiers sources dont la premire ligne est :
package nom-de-paquetage
Au sein dun paquetage, on a accs aux classes de ce paquetage et aux classes dclares public des autres paquetages, mais dans ce dernier cas il faut utiliser un nom
absolu :
nom-de-paquetage.nom-de-classe
De plus les classes compiles dun paquetage doivent tre places dans un rpertoire
de mme nom que le paquetage.
Il est possible de dsigner les classes par leur nom court, sans prciser le paquetage,
condition dutiliser la directive import :
import nom-de-paquetage.nom-de-classe
ou import nom-de-paquetage.* pour dsigner toutes les classes du paquetage.
TestPackage.java

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

Types et structures de contrle

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

Types et structures de contrle

3.1 Types en Java


En Java il y a une nette distinction entre les types primitifs (int, char, boolean ...)
et les types construits (de sorte classe ou tableau) : seuls les types primitifs ont la
notion de valeur offerte par le langage, alors que les types construits noffrent que
des objets, ncessairement manipuls par rfrence.
3.1.1 Types primitifs
char
caractres (Unicode, sur 16 bits, surensemble des caractres ASCII)
byte, short, int, long
nombres entiers 8, 16, 32 et 64 bits
boolean
boolens (valeurs true et false)
float, double
nombres flottants, simple et double prcision
oprateurs usuels :
+, -, *, /, %
==, !=
>, >=, <, <=
!, &, |, &&, ||

12

oprateurs arithmtiques pour entiers et flottants (% = modulo).


galit et non-galit, dfinis pour tout type primitif.
comparaisons arithmtiques pour entiers et flottants.
non, et, ou, et conditionnel, ou conditionnel, pour les boolens
(conditionnel : nvalue pas le second oprande si le premier
dtermine le rsultat).
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999

Types et structures de contrle

3.1.2 Types classes et tableaux


Les tableaux et les objets de type classe prsentent les points communs suivants : ils
sont toujours manipuls par rfrence, et leur dclaration ne fait quassocier lidentificateur une variable de nature rfrence initialise null. Pour que cet identificateur dsigne un objet, il faut lui affecter soit le rsultat dune cration, grce la
primitive new, soit un objet dj existant.
La dclaration dun identificateur de type classe a dj t vue. Un tableau se dclare
ainsi :
int T[]; tableau dentiers
Robot R[]; tableau dlments de type Robot.
La taille dun tableau nest pas indique dans la dclaration, mais au moment de la
cration dun tableau, de la faon suivante :
T=new int[20]; cre un tableau de 20 entiers, rfrenc par T
R=new Robot[5]; cre un tableau de 5 objets de type Robot, rfrenc par R
Les indices des tableaux commencent 0.
Laccs un lment se note ainsi : T[15]
On peut connatre la taille dun tableau T par lattribut T.length.
Java noffre pas de notion spcifique pour les tableaux plusieurs dimensions : on les
ralise simplement comme des tableaux de tableaux.
int M[][]; tableau de tableaux dentiers.

Gestion automatique de la mmoire : ramasse-miettes


En Java les objets construits (de type classe ou tableau) sont crs trs dynamiquement, par excution de new. Il sont crs dans le tas, zone mmoire gre par le langage, et leur place est automatiquement rcuprable lorsquils ne sont plus rfrencs.
Cette rcupration est effectue par un ramasse-miettes (garbage collector) qui, selon
les mises en uvre, est dclench lorsquil ny a plus de place ou bien fonctionne plus
ou moins en parallle avec lexcution des programmes.

3.2 Les rfrences


Les identificateurs de type classe ou tableau, que ce soient des identificateurs de variables ou de paramtres de mthode, dsignent toujours un objet travers une rfrence.
Lexemple suivant illustre ce que cela induit :
class TestReferences {
public static void main(String argv[]) {
Robot totor = new Robot(20,30,Robot.Nord);
Robot vigor;
System.out.println(totor.X);
imprime 20
vigor = totor;
vigor.avancer();
System.out.println(totor.X);
imprime 21
}}
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999

13

Types et structures de contrle

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Types et structures de contrle

Tableaux plusieurs dimensions


Le fait que Java considre les tableaux plusieurs dimensions comme des tableaux de
tableaux implique dune part que les sous-tableaux peuvent tre de tailles diffrentes,
par exemple :
int M[][];
M= new int[3][]; cre un tableau de 3 tableaux dentiers;
M[0]= new int[2]; cre le premier vecteur de M de taille 2
M[1]= new int[3]; cre le deuxime vecteur de M de taille 3
M[2]= new int[1]; cre le troisime vecteur de M de taille 1
et dautre part que plusieurs tableaux peuvent se partager le mme sous-tableau, ce
qui peut induire des effets de bords dont il faut se mfier, par exemple :
T=M[1]; T capte le deuxime vecteur de M
T[0]=12; ceci donne la valeur 12 T[0] mais aussi M[1][0].
M
12

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;

3.3 Chanes de caractres : String et StringBuffer


Java offre les chanes de caractres sous forme de deux classes String et StringBuffer. Ces classes sont riches en fonctionnalits diverses.
La classe String offre des objets chanes constants. Ceci signifie que si lon dclare
par exemple :
String ch1 = new String("bonjour");
lobjet cr, maintenant dsign par ch1, ne peut tre modifi, il vaudra toujours
"bonjour". Cela ne signifie nullement que ch1 soit une constante. Cest ici une
variable, capable de capter un autre objet, par exemple en laffectant :
String ch2 = new String("au revoir");
ch1 = ch2;
ch1 dsigne alors la chane "au revoir".
La forme new String("blabla") pour crer une chane de valeur "blabla"
peut tre abrge simplement en "blabla". Ainsi on peut dclarer directement :
String ch1 = "bonjour";
ou encore utiliser directement la notation de chane en paramtre effectif :
System.out.println("bonjour");
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999

15

Types et structures de contrle

Voici quelques mthodes utiles de la classe String :


static String valueOf(int i); chane qui reprsente lentier i
static String valueOf(boolean b); chane qui reprsente le boolen b
static String valueOf(t x); chane qui reprsente x de type primitif t.
Les mthodes ci-dessus ne portent sur aucun objet (mot cl static). Pour les utiliser, on les prfixe par le nom de la classe :
String.valueOf(12) rend la chane "12".
boolean equals(String s); teste lgalit entre this et s.
String concat(String s); rend la concatnation de this et s.
int length();
rend la longueur de this.
int indexOf(int c);
position de la premire occurrence du caractre de
code ASCII c, -1 si le caractre napparat pas.
char charAt(int i);
caractre en position i (numrots partir de 0)
La concatnation de chanes est galement offerte sous forme dun oprateur fonctionnel infixe not + :
s1+s2 : concatnation de s1 et s2
Un oprande de lopration + peut galement tre un caractre (il est converti en
chane de taille 1) ou un nombre (il est converti en une chane qui est sa notation dcimale).

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Types et structures de contrle

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);
};
}}

3.4 Donnes constantes - final


Les constantes sont dclares comme des composants de classe, en les qualifiant par
lattribut final. Lattribut final indique que lidentificateur ne peut apparatre en
partie gauche daffectation. Pour un composant de type primitif, ceci signifie que
lobjet dsign possde une valeur constante. Pour un composant de type classe ou
tableau cela signifie seulement que la rfrence associe lidentificateur est constante. Cela nempche pas lobjet dsign de subir des modifications.
Exemples :
final int nombreDeNains=7;
nombreDeNains vaut en permanence 7.
On ne peut pas crire : nombreDeNains=... ni nombreDeNains++, ....
final Robot totor= new Robot();
totor dsigne en permanence le mme objet de type Robot, celui cr la dclaration. On ne peut pas crire totor=.... En revanche, cet objet peut subir des modifications, par exemple on peut crire totor.X=12 ou encore totor.avancer().
La valeur dune constante nest attribue quune fois au cours de lexcution, et en des
endroits fixs par le langage :
soit lendroit de sa dclaration,
soit dans les constructeurs de la classe o elle est dfinie.
Cette seconde possibilit permet, par le paramtrage des constructeurs, dattribuer une
valeur diffrente un mme identificateur de constante pour des objets diffrents.
Si on a besoin de constantes non attaches des objets, on les dclare avec lattribut
static.
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999

17

Types et structures de contrle

Lexemple suivant illustre ces diverses possibilits :


class Robot {
static final int Nord = 1; static final int Est = 2;
static final int Sud = 3; static final int Ouest = 4;
int X; int Y; int orientation;
final int Xorig; final int Yorig;
final Robot collegue;
Robot(int x, int y, int o, Robot c) {
X=x; Y=y; orientation=o; Xorig=x; Yorig=y; collegue=c;
}
Robot(int x, int y, int o) {
X=x; Y=y; orientation=o; Xorig=x; Yorig=y; collegue=null;
}
...
void retourCaseDepart() {
X=Xorig; Y=Yorig;
}
void rejoindreCollegue() {
if (collegue!=null) {X=collegue.X; Y=collegue.Y;}
}}
Les constantes Nord, Sud, Est, Ouest ne sont pas associes aux objets, et sont
donc dclares static. Cette version de Robot offre des composants constants
associs chaque objet : la position initiale et un partenaire appel collegue. Ces
composants constants sont initialiss dans les constructeurs, de faon tre dpendants des paramtres de cration. Voici un exemple dutilisation de cette classe :
class TestConstantes {
public static void main(String argv[]) {
Robot totor = new Robot(20,30,Robot.Nord);
Robot vigor = new Robot(10,15,Robot.Sud,totor);
totor.avancer();
System.out.println("totor "+totor.X+" "+totor.Y);
totor.retourCaseDepart();
System.out.println("totor "+totor.X+" "+totor.Y);
System.out.println("vigor "+vigor.X+" "+vigor.Y);
vigor.rejoindreCollegue();
System.out.println("vigor "+vigor.X+" "+vigor.Y);
}
}

18

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Types et structures de contrle

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--

peu prs quivalent


peu prs quivalent

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}

Excute le bloc dinstructions correspondant la premire condition vraie.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

19

Types et structures de contrle

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];};

3.5.4 Instruction cas


switch (expr) {
case v1 : intructions1 break;
case v2 : intructions2 break;
...
default : intructions break;
}
expr est une expression de type byte, char, short, int ou long et les vi sont des
valeurs constantes du mme type que expr. Cette construction excute le bloc dinstructions correspondant la premire valeur vi gale expr.
La branche default est optionnelle et est excute si aucun des vi nest gal expr.
On peut regrouper plusieurs valeurs qui ncessitent le mme traitement :
case u : v : w : intructions break;

20

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Types et structures de contrle

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.

Pour linterprtation numrique de chanes de chiffres, la classe Integer offre la


mthode statique suivante :
int parseInt(String s) throws NumberFormatException
qui rend lentier reprsent en dcimal par la chane s.
Cette mthode dclenche une exception si s ne satisfait pas la syntaxe de reprsentation dcimale dun entier.
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999

21

Types et structures de contrle

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;};
}
}

3.7 Traitement dexception : throw-try-catch


La notion dexception permet de traiter de manire plus souple et plus lisible les cas
exceptionnels, essentiellement les cas derreurs. Si on traite les cas exceptionnels
comme des cas normaux, cela exige que les procdures rendent des rsultats supplmentaires, quil faut tester au moyen dinstructions conditionnelles, ... Ceci alourdit la
programmation au point que la logique de traitement des cas normaux se trouve noye
dans celle des cas exceptionnels.
22

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Types et structures de contrle

En Java, les cas exceptionnels peuvent donner lieu un dclenchement dexception.


Une exception peut tre dclenche par linterprteur du langage, par exemple lors
dune division par 0 ou dun accs hors des bornes dun tableau.
On peut aussi en dclencher par une instruction :
throw(e);
o e est un objet de classe Throwable, structure de donne qui contient des informations concernant la nature de lexception. Pour chaque catgorie dexception, il
existe une classe drive de Throwable (voir plus loin, chapitre sur lhritage), dont
le nom est de la forme xxxError ou xxxException, par exemple :
IOException : exception lie aux entres/sorties
NullPointerException : accs travers null
IndexOutOfBoundsException : accs hors des bornes dun tableau
NumberFormatException : syntaxe incorrecte de reprsentation dun nombre
par une chane de caractres.
...
Si une mthode est susceptible de dclencher une exception, cela doit tre indiqu
dans son profil, au moyen de la directive throws :
int ppp() throws xxxException { ... }

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

sein de la mthode p(),


void p() { ...
try { ... q() ...}
catch(xxxException e){ traitement de e }
}
soit propager lexception, en indiquant au moyen de la directive throws que

p() est elle-mme susceptible de dclencher cette exception :


void p() throws xxxException { ... q() ... }

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

23

Types et structures de contrle

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

double partir du sommet (5 dans lexemple).


Exercice 2
Rdiger une classe ListAssoc qui ralise une liste de paires de chanes
<cl,valeur> avec les services suivants :
teste si la liste est vide, ajoute une paire la liste, cherche la valeur associe une cl.
Exercice 3
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.
Dfinir les classes qui vous semblent judicieuses pour cette application. Donner les
profils des mthodes, choisir une reprsentation pour ces classes et programmer les
mthodes.
Exercice 4
Des liens fixes entre objets sont facilement reprsents par des identificateurs dots de
lattribut final. Comme exemple, on peut considrer la construction dun arbre
gnalogique.
Rdiger la classe Personne qui satisfait aux spcifications suivantes.
Une personne possde un nom, un pre, une mre et des enfants. Lors de la cration
dune personne on indique son nom, son pre et sa mre.

24

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Types et structures de contrle

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

On utilisera la classe ListePersonnes, liste dobjets de type Personne, dote


dun tat permettant le parcours :
ajoute(Personne p) : ajout dun lment,
debut() : positionnement du parcours au dbut de la liste,
avance() : fait avancer le parcours dune position,
Personne eltCourant() : lment courant du parcours,
boolean estEnFin() : test de fin de parcours.

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.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

25

Hritage

Hritage

4.1 Usage simple de lhritage : ajout de proprits


On a parfois besoin de dfinir un type dobjet similaire un type existant, avec quelques proprits supplmentaires. Lhritage permet de dfinir ce nouveau type sans
tout reprogrammer : il suffit de dclarer que le nouveau type hrite du prcdent et on
se borne rdiger ses fonctionnalits supplmentaires. Ceci constitue lutilisation la
plus simple de lhritage. Par exemple, on peut driver un type Salari partir
dun type plus gnral Personne :
class Personne {
String nom;
int nombreEnfants;
Personne(String n) { nom=n; nombreEnfants=0;}
}
class Salarie extends Personne {
int salaire;
int prime() { return 5*salaire*nombreEnfants/100;}
Salarie (String n, int s) {super(n); salaire=s;};
}
La notation class Salari extends Personne {...} indique que la
classe Salari hrite de la classe Personne. On dit galement que Salari est
une classe drive de Personne. Personne est une classe de base de Salari.
Le type Salari possde toutes les proprits du type Personne, mmes composants (donnes et mthodes), plus quelques nouvelles, le champ salaire et la fonction prime().
Une personne
nom
nombreEnfants

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Hritage

4.1.1 Accessibilit : public, protected, private


En ce qui concerne laccessibilit des composants dune classe de base partir des
textes des classes drives, le langage offre les quatre modes suivants :
aucun attribut : accessibles par les classes qui font partie du mme paquetage,
inaccessibles par les autres.
public :
accessibles par toutes les classes
protected : accessibles par toutes les classes drives,
et les classes du mme paquetage, inaccessibles par les autres
private : inaccessibles par toutes les classes
Lattribut protected permet de rendre accessibles certains membres pour la conception dune classe drive mais inaccessibles pour les utilisateurs de la classe de
base. Comme le montre lexemple suivant, laccs un composant protected est
interdit en situation dutilisation de la classe de base : depuis la classe B, bien quhritire de A, laccs a.JJ est interdit, car il sagit dune utilisation de A.
package bb;
import aa.*;

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

4.2 Compatibilit entre types


Le fait quun type B hrite dun type A signifie que B est un sous-type de A, cest-dire quun objet de type B est galement de type A. Par exemple, un Salari est
galement une Personne. Donc toute opration applicable au type A est galement
applicable au type B. Le type B est dit plus prcis que A, et A plus vague que B.
La rgle gnrale de compatibilit de types peut tre informellement nonce ainsi :
partout o une expression de type A est attendue, une expression de type plus prcis
que A est acceptable. En revanche, linverse est interdit. Cette rgle sapplique essentiellement en deux circonstances : en partie droite dune affectation ou bien en tant
que paramtre effectif de procdure. Par exemple, avec les dclarations suivantes :
Personne p ...;
Salari s ...;
void Q(Personne p) { ... };
ces instructions sont permises : p = s;

Q(s);

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

27

Hritage

Pendant lexcution, la variable p et le paramtre formel p sont des rfrences sur


lobjet prcis de type Salarie qui leur est assign.
Cependant p ne donne pas directement accs aux membres du type prcis
Salarie : on ne peut pas crire p.salaire, ni p.prime(). Le compilateur
refuse ces expressions, juste titre car lobjet dsign par p pourrait tre parfois du
type Personne, et ces accs nauraient aucune signification.
Pour profiter vraiment du fait que lobjet dsign par p est du type prcis Salarie,
il faut utiliser la notion de mthode virtuelle dcrite au paragraphe suivant.

Test de type : instanceof


Le langage Java permet de tester le type exact dun objet au moment de lexcution,
au moyen de la primitive :
expression instanceof classe
rend true si lexpression dsigne un objet de la classe indique, false sinon.
Ceci permet de faire jouer lhritage un rle similaire celui dunion de type : le
type de base devient dans ce cas lunion des types drivs. Cest un usage pratique
mais un peu dtourn de lhritage.

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.

1 - Programmer ces classes.

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Hritage

3 - On dispose de la classe ListDocuments qui permet de constituer une liste de


documents et de la parcourir.
class ListDocuments {
ListDocuments() {} // constructeur : liste vide
boolean estVide() // indique si la liste est vide
void ajoute(Document d) // ajoute un document la liste
void debut() // positionne le parcours au debut
void avance() // fait avancer le parcours
boolean estEnFin() // indique si le parcours est termine
Document elementCourant() // element courant du parcours
}

Rdiger la partie manquante du programme suivant qui doit compter le nombre de


livres et le nombre de dictionnaires prsents dans la bibliothque.
class TestdeClasse {
public static void main(String argv[]) {
ListDocuments bibli= new ListDocuments();
bibli.ajoute(new Livre("Le pere Goriot", "Balzac", 458));
bibli.ajoute( new Livre("Nounours", "Chantal Goya", 14));
bibli.ajoute( new Dictionnaire("Larousse", 4500));
bibli.ajoute( new Livre("Tintin", "Herge", 62));
bibli.ajoute( new Dictionnaire("Petit Robert", 5000));
int nbLivres=0; int nbDicos=0;
...
System.out.println("nombre de livres = "+nbLivres);
System.out.println("nombre de dictionnaires = "+nbDicos);
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

29

Hritage

4.3 Mthodes virtuelles


4.3.1 Dfinition de mthodes virtuelles
Les mthodes virtuelles permettent de prvoir des oprations similaires sur des objets
despces diffrentes. On les appelle galement mthodes diffres pour insister sur
le fait quelles sont destines tre dfinies ou redfinies pour chaque sous-espce
dobjets que lon inventera ultrieurement.
En Java, toute mthode est potentiellement virtuelle, sauf si on lui donne lattribut
final, auquel cas elle ne pourra tre redfinie par les classes drives.
En Java, une classe peut annoncer une mthode sans la dfinir. Une telle classe est dite
abstraite et elle doit tre introduite par le mot cl abstract. Les mthodes qui ne
sont pas dfinies sont galement qualifies abstract et seul leur profil est indiqu.
abstract class A {
...
abstract void P();
...
void Q() {...}
}

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Hritage

class Rectangle extends Figure {


double LX; double LY; // hauteur, largeur
Rectangle(Point coin,double lx,double ly) {
super(coin); LX=lx; LY=ly;
}
double perimetre() {return 2*(LX+LY);}
}
class Polygone extends Figure {
...
}
Les diverses espces de figures sont ralises sous forme de classes hritires de
Figure et chacune dfinit une version de la fonction perimetre() :
Figure
orig
perimetre

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

31

Hritage

Mme si la mthode perimetre tait dfinie au niveau de la classe Figure, cest


la mthode de la classe prcise de lobjet, Cercle ou Rectangle, qui serait appele. Au moment de lappel f.perimetre(), la mthode convenable est appele
grce une information mmorise dans chaque objet. Cette information nest pas
trs coteuse : cest gnralement un pointeur sur une description de la classe de
lobjet.
Exercice 6
Indiquer les rsultats affichs par le programme suivant :
class A { void p() {System.out.print(" A ");} }
class B extends A { void p() {System.out.print(" B ");} }
class TestVirtualite {
static void q(A a) {a.p();}
public static void main(String argv[]) {
A a= new A(); B b=new B();
a.p(); q(a);
a=b;
a.p(); q(a); q(b);
}
}

4.4 Quelques utilisations des mthodes virtuelles


Parmi les nombreuses utilisations possibles des mthodes virtuelles, on peut signaler
les suivantes :
manipulation de collections dobjets de types similaires mais distincts,
rdaction de modules logiciels extensibles,
utilisation de plusieurs reprsentations concrtes dun mme type abstrait.
Collection de donnes de types similaires
Comme on vient de le voir avec les diverses catgories de figures, les mthodes virtuelles permettent de manipuler de faon homogne des collections dobjets de types
similaires mais distincts. Les diverses varits (Cercle, Rectangle, ...) hritent
dun type vague unique reprsent par une classe de base (Figure). Pour respecter
les rgles de typage usuelles, les collections de tels objets (listes, ensembles, tables,
...) sont gres au moyen didentificateurs dclars du type vague. Ainsi, bien que lon
ignore le type prcis des objets dsigns, lorsquon appelle une mthode de la classe,
la mthode du type prcis de lobjet est automatiquement appele.
Dans ce genre dutilisation, jamais aucun objet de la classe de base nest cr. La
classe de base ne sert qu regrouper les diverses espces. Cest un peu comme dans la
nature, il nexiste pas danimal mammifre, un animal est toujours dune espce
prcise, vache, chien, chat... Une telle classe est qualifie dabstraite. Il est prfrable de ne pas dfinir les mthodes virtuelles dans la classe de base, en utilisant
lattribut abstract, ainsi le compilateur refusera la cration dobjets de ce type.
32

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Hritage

Modules logiciels extensibles


Les mthodes virtuelles facilitent la rdaction de logiciels extensibles, cest--dire qui
peuvent tre modifis et enrichis ultrieurement. Lhritage et les mthodes virtuelles
permettent deux formes dextensions :
Modification de fonctionnalit : tant donn une classe T1 dote de mthodes
virtuelles, un programmeur ultrieur peut en driver par hritage une classe T2
mieux adapte ses besoins en redfinissant certaines de ces mthodes.
Extension de programmes dj conus de nouvelles varits dobjets : si
le module existant structure et manipule des objets dun type vague T dot de
mthodes virtuelles, il est possible de rajouter des varits du type T celles
dj existantes sous la forme de classes drives T1, T2 ... Par exemple, disposant dun logiciel qui manipule des objets du type Figure prcdent, il est
possible, bien aprs la conception de ce module, et sans le modifier, de rajouter
de nouvelles espces de figures.
Mlange de plusieurs reprsentations dun mme type abstrait dobjets
On peut faire cohabiter plusieurs reprsentations dun mme type abstrait, tout en
manipulant les objets de ce type de faon homogne, indpendamment de leur reprsentation. Lintrt est ici de pouvoir adapter la reprsentation divers cas spciaux
pour amliorer les performances en espace ou en temps. Par exemple, pour des listes,
on peut envisager les reprsentations suivantes :
La premire au moyen dun tableau et dune taille effective. Ceci est performant
en temps, mais offre des listes de taille limite.
La seconde au moyen de maillons crs dynamiquement au fur et mesure des
besoins. Cette solution est plus lente, mais ne limite pas la taille des listes.
Pour permettre la cohabitation de multiples reprsentations, on peut procder ainsi :
on dfinit une classe T ne comportant aucune structure de donne, qui reprsente le
type abstrait. Les reprsentations concrtes sont ralises par autant de classes T1, T2
... drives de T. Les mthodes de T dont la ralisation dpend des structures de donnes sont virtuelles, et on en rdige la version convenable au sein de chaque classe
drive.
Souvent, le type T possde des oprations primitives dont la programmation ncessite
la connaissance des structures de donnes concrtes, et des oprations secondaires que
lon peut entirement exprimer au moyen des oprations primitives. On peut avantageusement programmer ces oprations secondaires directement au niveau du type abstrait T, en utilisant les oprations primitives virtuelles, seules ces oprations primitives
tant programmes au niveau des ralisations concrtes.
Lorsque le rsultat dune opration est lui-mme du type abstrait T, cela pose quelques problmes, car il doit tre en fait dun type prcis T1 ou T2, car il nexiste pas
dobjet strictement de type T. Lopration doit produire son rsultat par effet de bord,
cest--dire en agissant sur un objet, soit lobjet courant, soit un objet pass explicitement en paramtre.
Par exemple, les nombres complexes admettent deux reprsentations traditionnelles :
cartsienne (partie relle et partie imaginaire, X+iY) ou polaire (rayon et angle, ei).
Pour mler ces deux reprsentations en un type gnral Complexe, lopration
daddition devra avoir un des profils suivants :
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999

33

Gnricit - Ralisation imparfaite en Java

class Complexe {
...
(1) void add(Complexe c1)
qui ralise this = this+c1
(2)

void add(Complexe c1, Complexe c2)


qui ralise this = c1+c2

(3)

static void add(Complexe c1, Complexe c2, Complexe c3)


qui ralise c1=c2+c3
dans ce cas la mthode est static car elle nest pas associe un objet.

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.

Gnricit - Ralisation imparfaite en Java

La gnricit est la possibilit de paramtrer la dfinition de modules logiciels. Un


cas frquent et trs utile de gnricit consiste dfinir des types paramtrs par
dautres types. Par exemple, on peut ainsi dfinir le type Pile(T), les piles dlments de type quelconque T. Une telle dfinition sappelle un type gnrique. On peut
ensuite utiliser ce type gnrique pour disposer des piles dentiers, Pile(int), de
caractres, Pile(char), ou de tout autre type pass en paramtre effectif.
Java noffre pas vraiment les types gnriques, mais il permet de les raliser, en respectant certaines conventions, au moyen de la classe Object et en pratiquant le
typage forc (coercition explicite ou cast).
La classe Object est la classe dont hritent implicitement toutes les classes programmes en Java. Ainsi pour structurer des donnes de type quelconque, il suffit de
structurer des objets de type Object. Par exemple, la classe pile dlments de
nimporte quel type se programme ainsi :
34

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Gnricit - Ralisation imparfaite en Java

class Pile { // pile dobjets de type quelconque


private int s;
private Object P[]= new Object[100];
Pile() {s=-1;}
void empiler(Object e) { s++; P[s]=e;}
void depiler() { s--;}
Object sommet() {return P[s];}
boolean estVide() {return s==-1;}
}
On peut ensuite crer de telles piles et y ranger des objets de type quelconque. Par
exemple on peut crer une pile dtudiants. Mais il ne faut pas oublier que cest par
discipline que lon sastreint nempiler que des objets de mme type (des tudiants
dans notre exemple).
class TestPile {
public static void main(String argv[]) {
Etudiant toto = new Etudiant("toto","ici");
Etudiant alfred = new Etudiant("alfred","labas");
Etudiant jules = new Etudiant("jules","loin");
Pile pEtu = new Pile(); // pile detudiants
pEtu.empiler(toto);
pEtu.empiler(jules);
pEtu.empiler(toto);
pEtu.empiler(alfred);
while (!pEtu.estVide()) {
Etudiant etu = (Etudiant) pEtu.sommet(); // coercition
System.out.println(etu.nom + " " + etu.adresse);
pEtu.depiler();
};
}
Pour Java, les lments sont de type vague object, et rien ninterdit dempiler des
objets de types diffrents, et cela peut se concevoir si on sait retrouver le bon type au
moment de lexploitation des lments.
De toute faon, pour exploiter les lments, il faut leur redonner leur type prcis, et
cela se fait au moyen dune opration de coercition explicite (cast), dont la forme
gnrale est :
(type) expr : force lexpression tre considre comme tant du type indiqu
Cest une opration puissante et peu coteuse (pour des objets de type classe, elle ne
fait quempcher le compilateur de signaler une erreur de type), mais trs dangereuse
si elle nest pas pratique avec discipline.
Dans lexemple, pour que le sommet de la pile soit vu comme un tudiant, il faut utiliser la coercition :
(Etudiant) pEtu.sommet()

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

35

Interfaces - polymorphisme

Le principe prcdemment dcrit fonctionne bien pour des structurations dobjets. Il


se prte mal des structurations de valeurs, car les lments ainsi structurs doivent
driver du type Object. Or, les types primitifs (boolean, byte, char, short,
int, long, float, double) ne drivent pas dObject.
Pour structurer des lments dun type primitif, il faut encapsuler ce type primitif dans
une classe, et, ce qui est (scandaleusement) coteux, crer un objet par lment, l ou
une simple valeur aurait suffi. Par exemple, pour faire une pile dentiers :
class Integer { // encapsule le type int
private int v;
Integer(int i) {v=i;}
int intValue() {return v;}
}
Pile pEntier = new Pile(); // pile dentiers
pEntier.empiler(new Integer(12));
pEntier.empiler(new Integer(14));
pEntier.empiler(new Integer(678));
while (!pEntier.estVide()) {
int i = ((Integer) pEntier.sommet()).intValue();
// coercition + prise de valeur
System.out.print(" "+ i);
pEntier.depiler();
};

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

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] =

peuple = new Personne[4];


new Personne("toto", 25, 80);
new Personne("tutu", 53, 65);
new Personne("tata", 15, 47);
new Personne("jojo", 12, 30);

Utiliser AlgoGene pour trier cette population selon leur ge puis selon leur poids.

38

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

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.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Interfaces utilisateurs : paquetage AWT

Interfaces utilisateurs : paquetage AWT

7.1 Organisation gnrale des interfaces utilisateurs


Java offre, par son paquetage AWT (Abstract Window Toolkit), le moyen de crer des
interfaces base de fentres et de clics sur des boutons. Le schma suivant montre la
hirarchie des classes qui permettent de construire de telles interfaces.
Component
classe abstraite dont drivent tous les composants

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

Interfaces utilisateurs : paquetage AWT

import java.awt.*; import java.awt.event.*;


class FenetreCompteur extends Frame {
int compteur;

Button boutonIncr=
Button boutonDecr=
Button boutonQuit=

new Button("+");
new Button("-");
new Button("quit");

TextField affichageCompteur = new TextField(7);

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();}
};

class ActionQuit implements ActionListener {


public synchronized void actionPerformed(ActionEvent e)
{System.exit(0);}
};
void afficherCompteur() {
affichageCompteur.setText(String.valueOf(compteur));
}

6
7

public FenetreCompteur(String nom) { // constructeur


super("compteur " + nom); compteur=0;
Placement.p(this,boutonIncr,1,1,1,1);
Placement.p(this,boutonDecr,1,2,1,1);
Placement.p(this,boutonQuit,1,3,1,1);
Placement.p(this,affichageCompteur,2,1,1,2);
boutonIncr.addActionListener(new ActionIncr());
boutonDecr.addActionListener(new ActionDecr());
boutonQuit.addActionListener(new ActionQuit());
pack(); setVisible(true);
afficherCompteur();
}
}
public class TestAWT {
static public void main(String argv[]) {
new FenetreCompteur("CPT1");
}
}

Remarque : le processus qui soccupe de donner vie linterface utilisateur (afficher


les fentres, dtecter les actions sur la souris et gnrer les vnements) est de mme
priorit que celui qui excute la mthode main(). Or, dans la plupart des versions de
Java, le systme dallocation du processeur aux processus nest pas un systme partage de temps, mais un systme qui conserve le processeur pour le processus le plus
42

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Interfaces utilisateurs : paquetage AWT

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.

7.2 Gestion des vnements


Les vnements sont classs par thmes donnant lieu chacun une classe. Voici quelques unes de ces classes :
ActionEvent
MouseEvent
KeyEvent
FocusEvent
TextEvent
WindowEvent
etc ...

composant gnrateur et signification


Button : cliquage, TextField : touche Enter
Component : mouvements et cliquage de souris
Component : enfoncement et relchement de touche
Component : entre et sortie du curseur de souris
TextField, TextArea : modification du texte
Window : iconification, activation, ouverture, fermeture

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)

ActionEvent class TraiteEvtDeComp


comp
implements ActionListener {
actionPerformed() { ... }
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

43

Interfaces utilisateurs : paquetage AWT

7.3 Placement des composants


Le placement des composants dans un rceptacle (Container) se fait au moyen de
gestionnaires de placement (layout manager). Il en existe plusieurs :
BorderLayout, place les composants dans cinq zones, le centre et les 4 cts.
CardLayout, dfinit des fiches superposes.
FlowLayout, range les composants ligne par ligne, de gauche droite.
GridLayout, range les composants dans un tableau deux dimensions.
GridBagLayout, gestionnaire sophistiqu qui range les composants dans une grille
topologique en X (horizontalement) et en Y (verticalement). Les emplacements sont
dsigns par des entiers. Ces entiers ne signifient nullement une mesure de la position
mais servent positionner les composants les uns par rapport aux autres : horizontalement la position i+1 est droite de la position i, et verticalement j+1 est en dessous de
j. Dans cette grille, un composant occupe une certaine zone dont la largeur et la hauteur sont galement indiques en nombre de positions de cellule. Pour chaque composant, on indique comment il est cadr dans sa zone, au centre, gauche, droite, en
haut ou en bas et quelles sont les marges autour du composant dans sa zone. On indique comment la zone alloue au composant se comporte lors dune modification de
taille de son rceptacle : deux nombres rels fixent le taux dextension relative de la
zone alloue au composant par rapport aux autres composants du mme rceptacle.
x
On fixe galement les directions
cont 0
dextension du composant dans sa
1
2
3
4
zone lorsquil dispose de plus de
0
y
place que ncessaire : aucune extension, extension en largeur, en hauteur
1
h
comp
ou dans les deux directions.
2
w
Ce gestionnaire est trs puis3
sant, mais difficile utiliser.
Cest pourquoi nous avons ici encapsul son utilisation dans une classe appele Placement qui offre les services les plus utiles. Lappel gnral est :
Placement.p(cont, comp, x, y, w, h, cadrage, t, l, b, r, wx, wy, fill)
cont : rceptacle de type Container dans lequel est plac le composant
comp : le composant
x, y : position du coin nord-est du composant
w, h : largeur et hauteur de la zone alloue au composant
cadrage : cadrage du composant dans sa zone, valeurs possibles :
GridBagConstraints.CENTER
au centre
GridBagConstraints.NORTH
en haut
GridBagConstraints.EAST
droite
GridBagConstraints.SOUTH
en bas
GridBagConstraints.WEST
gauche
t, l, b, r : marge autour du composant dans sa zone, en haut, gauche, en bas, droite
wx, wy : poids du taux dextension horizontale et verticale de la zone alloue
fill : direction(s) dextension du composant dans sa zone, valeurs possibles :
GridBagConstraints.NONE
aucune extension
GridBagConstraints.HORIZONTAL horizontal
GridBagConstraints.VERTICAL
vertical
GridBagConstraints.BOTH
les deux directions
44

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Interfaces utilisateurs : paquetage AWT

La classe Placement offre galement des versions simplifies de la mthode p(),


obtenues en fixant des valeurs par dfaut pour certains paramtres :
class Placement {
static GridBagLayout placeur= new GridBagLayout();
static GridBagConstraints c = new GridBagConstraints();
// procedure generale de placement
//-------------------------------public static void p( Container cont, Component comp,
int x, int y, int w, int h, int cadrage,
int t, int l, int b, int r, double wx, double wy, int fill) {
cont.setLayout(placeur);
c.gridx=x; c.gridy=y; c.gridwidth=w; c.gridheight=h;
c.fill=fill;
c.anchor=cadrage;
c.weightx=wx; c.weighty=wy;
c.insets = new Insets(t,l,b,r);
placeur.setConstraints(comp, c); cont.add(comp);
};
// placement dun composant qui ne grossit pas
//-------------------------------------------public static void p(Container cont, Component comp,
int x,int y, int w,int h, int cadrage, int t,int l,int b,int r) {
p(cont, comp, x, y, w, h, cadrage, t, l, b, r,
0.0, 0.0, GridBagConstraints.NONE);
};
// placement dun composant sans marges qui ne grossit pas
//-------------------------------------------------------public static void p(Container cont, Component comp,
int x, int y, int w, int h, int cadrage) {
p(cont, comp, x, y, w, h, cadrage,
0, 0, 0, 0, 1.0, 1.0, GridBagConstraints.NONE);
};
// placement au centre dun composant sans marges qui ne grossit pas
//-----------------------------------------------------------------public static void p(Container cont, Component comp,
int x, int y, int w, int h) {
p(cont, comp, x, y, w, h, GridBagConstraints.CENTER,
0, 0, 0, 0, 1.0, 1.0, GridBagConstraints.NONE);
};}

7.4 Quelques mthodes des classes de AWT


abstract class Component {
void setVisible(boolean ouiNon); rend visible ou invisible le composant
Container getParent(); fentre parente du composant
Dimension preferredSize(); taille prfre du composant
Dimension getSize();
setSize(Dimension d);consulte / change la taille du composant
Color getBackground();
void setBackground(Color c); consulte / change la couleur du fond
Color getForeground();consulte / change la couleur daffichage
void setForeground(Color c);
...
}
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999

45

Interfaces utilisateurs : paquetage AWT

abstract class Container extends Component {


Component add(Component c);
ajoute le composant en tant que membre visuel du container
void setLayout(LayoutManager m);
associe ce gestionnaire de placement au container
...
}

class Window extends Container{


Window(Frame parent);
void pack(); arrange au mieux les composants dans la fentre
synchronized void dispose(); dtruit la fentre et libre les ressources quelle dtient
...
}

class Frame extends Window implements MenuContainer {


Frame(String titre);
Frame();
int getCursorType();
void setCursor(int typeCurseur); consulte / change le type de curseur
final static int
CROSSHAIR_CURSOR, DEFAULT_CURSOR,
E_RESIZE_CURSOR, W_RESIZE_CURSOR, ...
HAND_CURSOR, TEXT_CURSOR, WAIT_CURSOR;
codage des sortes de curseurs
...
}

class Dialog extends Window {


Dialog(Frame parent, String titre, boolean modal);
si modal=true : cette fentre de dialogue accapare lattention de
linterprteur Java tant quelle nest pas dtruite par Dispose()(peu utile)
...
}

class FileDialog extends Dialog {


FileDialog(Frame parent, String titre);
FileDialog(Frame parent, String titre, int load_save);
final static int LOAD, SAVE; modes du dialogue
String getDirectory();
String setDirectory(); consulte / change le nom de rpertoire
String getFile();
String setFile(); consulte / change le nom de fichier
String getFilenameFilter();
String setFilenameFilter();
consulte / change le filtre de prsentation des noms de fichiers
...
}

class Panel extends Container {


Panel();
...
}

46

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Interfaces utilisateurs : paquetage AWT

class Button extends Component {


Button(String label);
Button();
...
}
class Checkbox extends Component {
Checkbox(String label);
Checkbox();
boolean getState();
void setState(boolean etat);
consulte / change ltat de la bote cocher
...
}
class Label extends Component {
Label(String label);
Label();
String getText();
void setText(String t);
consulte / change le texte affich
...
}

class TextComponent extends Component {


String getText();
void setText(String t); consulte / change le texte affich
String getSelectedText(); dlivre la zone de texte slectionne sur lcran
int getSelectionStart();
int getSelectionEnd(); dlivre lindice de dbut / de fin de la zone texte slectionne
void setEditable(boolean ouiNon);
rend la zone de texte ditable ou non depuis le clavier
...
}
class TextField extends TextComponent {
TextField(int longueur);
TextField(String texteInitial);
TextField(String texteInitial, int longueur);
...
}

class TextArea extends TextComponent {


TextArea(int nbLignes, int nbColonnes);
TextArea(String texteInitial);
TextArea(String texteInitial, int nbLignes, int nbColonnes);
...
}

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

Paralllisme en Java : Threads

Paralllisme en Java : Threads

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.

8.1 Cration de processus


Java offre deux faons de crer des processus : par cration dun objet qui hrite de la
classe Thread, ou par excution de la primitive new Thread() sur un objet qui
implmente linterface Runnable. Nous nutiliserons ici que la premire forme, car
la seconde najoute rien dessentiel. La classe Thread dclare une mthode virtuelle
run() dans laquelle on rdige le programme principal du processus. Lexemple suivant cre deux processus, un sur le modle A qui imprime A0 ... A7, et un autre sur le
modle B qui imprime B0 ... B7. La cration est faite par new A() et new B().
Cependant cette cration ne lance pas par elle-mme lexcution. Il faut de plus appeler la mthode start() de la classe Thread qui lance effectivement le processus
sur lexcution de la mthode run().
class A extends Thread {
public void run() {
for(int i=0; i<8; i++) { System.out.print("A"+i+" ");
try {sleep(100);} catch (InterruptedException e) {};
};
}
}
class B extends Thread {
public void run() {
for(int i=0; i<8; i++) { System.out.print("B"+i+" ");
try {sleep(200);} catch (InterruptedException e) {};
};
}
}
public class TestThread1 {
static public void main(String argv[]) {
new A().start(); new B().start();
}
}
Les processus sont excuts en parallle. Dans le cas (usuel) dune machine nombre de processeurs limit (gnralement limit 1), un mcanisme de partage des processeurs fait progresser tour tour les processus. Pour permettre dintervenir sur
lallocation du processeur, Java offre un systme de priorit que nous nutiliserons pas
ici.
Dans lexemple, on a rgl la vitesse dexcution des processus au moyen de la
mthode sleep(int t) qui met en attente un processus pendant t milli-secondes.
Il en rsulte lentrelacement suivant des impressions de A et de B :
A0 B0 A1 B1 A2 A3 B2 A4 A5 B3 A6 A7 B4 B5 B6 B7
48

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Paralllisme en Java : Threads

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();
} }

8.2 Exclusion et moniteurs : synchronized


8.2.1 Exclusion
Pour que plusieurs processus puissent se partager sainement des objets afin de cooprer ou dutiliser des ressources, il faut pouvoir limiter le paralllisme en assurant
quun certain objet ne subisse pas en mme temps plusieurs squences dactions :
cest ce quon appelle assurer lexclusion mutuelle de ces actions quant cet objet.
Lexemple suivant illustre un besoin dexclusion. Deux processus impriment lun des
BONJOUR et lautre des AU REVOIR.
public class TestThread3 {
static public void main(String argv[]) {
new Impr("BONJOUR ").start();
new Impr("AU REVOIR ").start();
}
}
class Impr extends Thread {
String txt;
public Impr(String t){txt=t;}
public void run() {
for(int j=0; j<2; j++) {
for(int i=0; i<txt.length(); i++) {
try {sleep(100);} catch (InterruptedException e) {};
System.out.print(txt.charAt(i));
};
};
} }
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999

49

Paralllisme en Java : Threads

Cette programmation nassure pas lexclusion de limpression pour toute la dure de


limpression dune chane significative. On obtient le rsultat suivant dans lequel
apparat un mlange incontrl des caractres des deux textes :
BAOUN JROEUVRO IBRO NAJUO URRE VOIR

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Paralllisme en Java : Threads

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));
};
}
}

class Impr extends Thread {


String txt;
static MoniteurImpression m1 = new MoniteurImpression();
public Impr(String t){txt=t;}
public void run() {
for(int j=0; j<2; j++) { m1.imprTexte(txt);};
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

51

Paralllisme en Java : Threads

8.3 Attente explicite : wait - notify


8.3.1 Attente quune condition soit satisfaite : wait()
lintrieur dun moniteur (cest dire dune mthode ou dun bloc qualifi par
synchronized), un processus peut se mettre en attente au moyen de wait().
Cette primitive est susceptible de dclencher, pendant lattente, une exception de type
InterruptedException, ce qui oblige souvent utiliser la forme :
try {wait();} catch(InterruptedException e) {...};
Lorsquun processus excute wait() sur un objet obj, cela relche lexclusion sur
lobjet obj, de sorte que dautres processus puissent acqurir cette exclusion et venir
le rveiller.
8.3.2 Rveil des processus : notify() et notifyAll()
Un processus sort de lattente lorsquun autre processus excute notify() au sein
de ce mme moniteur. Cette primitive existe sous deux formes :
notify() : relance un processus en attente dans ce moniteur,
notifyAll(): relance tous les processus en attente dans ce moniteur.
chaque moniteur est associe une file dattente de processus en attente. Il semblerait
que les processus soient sortis de la file selon leur ordre darrive, mais cela nest pas
clairement spcifi dans les documents actuels sur le langage : il est donc prfrable
de ne pas en tenir compte dans la programmation. On peut excuter notify()
mme si aucun processus nest en attente : cela ne fait rien dans ce cas.
Lexemple suivant illustre la programmation du classique tampon producteur-consommateur une place.
class MoniteurProdCons {
String tampon; boolean estVide=true;
synchronized void prod(String m) {
if(!estVide){ System.out.println("PRODUCTEUR ATTEND");
try {wait();} catch(InterruptedException e) {};
};
System.out.println("PRODUIT
: " + m);
tampon=m; estVide=false; notify();
}
synchronized String cons() {
if(estVide){ System.out.println("CONSOMMATEUR ATTEND");
try {wait();} catch(InterruptedException e) {};
};
System.out.println("CONSOMME : " + tampon);
String resul=tampon; estVide=true; notify();
return resul;
}
}
52

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Paralllisme en Java : Threads

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

Paralllisme en Java : Threads

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Paralllisme en Java : Threads

les moniteurs de Hoare, la priorit est officiellement attribue au processus rveill :


cela peut sembler tre une sur-spcification dont on se passerait volontiers, mais cest
ncessaire pour assurer la vrit de lassertion associe (mentalement par le programmeur) la condition derrire tout attendre(), car si le processus rveilleur est poursuivi aprs le rveil, son action peut rendre caduque cette assertion. Avec les
moniteurs de Java, la rgle na pas besoin dtre aussi prcise car il ny a quune condition qui nest pas en gnral associe une assertion prcise et les assertions sont
destines tre re-testes par les processus rveills. Exprimentalement, la priorit
semble tre donne au rveilleur, qui continue donc aprs le notify(). Un processus rveill nest repris que lorsque le rveilleur quitte lexclusion, cest--dire sort du
moniteur ou bien se met en attente par wait() dans ce moniteur. De toutes faons, il
est toujours prfrable de programmer avec le moins dhypothses possibles sur la
smantique du langage quant ces dtails.

8.4 Rflexions sur lusage des processus


8.4.1 Nature des processus
Un processus nest pas un objet, ou du moins il nest pas un objet comme les autres.
Cest une excution, une activit, donc quelque chose de plus difficile percevoir
quun objet. Certes il y a bien un objet associ au processus, lobjet de classe Thread
qui lui donne naissance. Mais cet objet nest que le point de dpart du processus, son
programme principal. Le propre dun processus cest de se promener dobjet en objet.
Une image assez juste est la suivante : les objets sont des fleurs et les processus sont
des abeilles qui butinent ces fleurs.

Cela se peroit concrtement dans le langage : il y a la notion de processus courant,


accessible par la mthode currentThread(). Cest une mthode statique de la
classe Thread, qui rend en rsultat lobjet de classe Thread associ au processus
qui lexcute. Cette mthode sutilise donc gnralement sous la forme :
... Thread.currentThread() ...
Ainsi il y deux notions de choses courantes bien distinctes quil ne faut pas confondre :
this : lobjet courant, sur lequel a lieu lexcution (la fleur)
Thread.currentThread() : le processus qui excute (labeille).
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999

55

Paralllisme en Java : Threads

Certaines primitives concernant les processus sont des mthodes de la classe


Thread, par exemple :
static void sleep(int t) throws InterruptedException
attend t millisecondes
void join() throws InterruptedException
attend quun processus termine
int getPriority()
priorit courante du processus pour lusage du processeur
void setPriority(int p) throws IllegalArgumentException
change la priorit du processus
void stop()
termine un processus
...
Pour utiliser ces mthodes, il faut employer une des formes :
Thread.sleep(100)
Thread.currentThread().getPriority()
Dautres primitives concernant les processus sont des mthodes de la classe Object,
ce qui signifie quelles sappliquent depuis nimporte quelle classe, car toutes drivent
de la classe Object. Ce sont notamment :
wait(), notify(), notifyAll().
Ces mthodes utilisent le verrou dexclusion et la file dattente de condition associs
tout objet. Si on nindique rien de plus, elles utilisent donc ces lments associs
lobjet courant, this. Cependant, le langage tant totalement orthogonal, rien
nempche de les utiliser sur dautres objets :
synchronized (unObjet) {
... unObjet.wait(); ... unObjet.notify(); ...
}
Pour des raisons de structuration des programmes, il vaut mieux si possible viter
cette forme.
8.4.2 Intrts des processus
Lusage des processus offre divers avantages, souvent mlangs, de sorte quil est difficile de dire si on les utilise pour telle raison ou pour telle autre. On peut distinguer
trois grandes catgories dusages :
Interactivit : lextrieur tant par nature parallle, il est souhaitable que lintrieur le soit galement. Par exemple, aprs avoir lanc une copie de disquette ou
le chargement dune image davion depuis le Web, il est agrable de ne pas tre
bloqu et pouvoir faire de ldition de texte.
Structure de contrle plus riche : chaque processus possde son propre contexte de travail, son tat de contrle qui indique o il en est dans le programme,
cest--dire un compteur ordinal au sens large. Avec plusieurs processus on
dispose donc de plusieurs compteurs ordinaux, et donc dune plus grande
richesse dexpression. Dans ce cas ce nest pas le paralllisme qui est exploit :
ces processus ne servent qu attendre et nont jamais loccasion de tourner en
mme temps. En revanche, ils peuvent attendre tout en conservant leur tat de
contrle et cela permet une meilleure modularit des textes des programmes.
Calcul parallle : les processus permettent dexprimer des algorithmes parallles de calcul, et on peut esprer que les performances soient meilleures si le
paralllisme est effectif sur plusieurs processeurs.
56

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Paralllisme en Java : Threads

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();

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

57

Paralllisme en Java : Threads

class FenetreDeControle extends Frame {


Button boutonPaye=
new Button("Paye du personnel");
class ActionPaye implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
(new PayeDuPersonnel()).start(); // lance la paye du personnel
}
1
};
Button boutonEdit=
new Button("Edit");
class ActionEdit implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
new Edit(); // cree une edition de texte
}
};
Button boutonQuitter=
new Button("Quitter");
class ActionQuitter implements ActionListener {
public synchronized void actionPerformed(ActionEvent e)
{System.exit(0);}
};
public FenetreDeControle() {
super("CONTROLE");
Placement.p(this,boutonPaye,
0,0,1,1);
Placement.p(this,boutonEdit,
0,1,1,1);
Placement.p(this,boutonQuitter, 0,2,1,1);
boutonPaye.addActionListener(new ActionPaye());
boutonEdit.addActionListener(new ActionEdit());
boutonQuitter.addActionListener(new ActionQuitter());
pack(); setVisible(true);
}
}

class PayeDuPersonnel extends Thread {


class FenetrePaye extends Frame {
TextField txt= new TextField(40);
public FenetrePaye() {
super("PAYE DU PERSONNEL");
Placement.p(this, txt, 0,0,1,1);
pack(); setVisible(true);
}
}
FenetrePaye f= new FenetrePaye();}

public void run() {


for (int i=100; i>0; i--) {
f.txt.setText("encore " + i + " secondes avant la paye");
try{sleep(1000);} catch(Exception e){};
}; f.dispose();
}
}

58

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Paralllisme en Java : Threads

class Edit {

class FenetreEdition extends Frame {


TextArea page= new TextArea(10,80);
String selection;
Button boutonCopier=
new Button("copier");
class ActionCopier implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
selection=page.getSelectedText();
}
};
Button boutonColler=
new Button("coller");
class ActionColler implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
int i = page.getSelectionStart();
int j = page.getSelectionEnd();
page.replaceRange(selection,i,j);
}
};
Button boutonFermer=
new Button("fermer");
class ActionFermer implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
FenetreEdition.this.dispose();
}
};
public FenetreEdition() {
super("EDIT");
Placement.p(this, boutonCopier, 0,0,1,1);
Placement.p(this, boutonColler, 1,0,1,1);
Placement.p(this, boutonFermer, 2,0,1,1);
Placement.p(this, page, 0,1,3,1);
boutonCopier.addActionListener(new ActionCopier());
boutonColler.addActionListener(new ActionColler());
boutonFermer.addActionListener(new ActionFermer());
pack(); setVisible(true);
}
}
FenetreEdition f = new FenetreEdition();
}

public class TestInteract {


static public void main(String argv[]) {
new FenetreDeControle();
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

59

Paralllisme en Java : Threads

Structure de contrle plus riche


Lexemple suivant illustre comment lusage de processus peut aider structurer agrablement une application.
Il sagit dun schma assez frquent : lapplication permet de construire une Scne
forme de cercles de tailles et de coordonnes varies. La scne se construit au moyen
dune fentre SCENE illustre ci-dessous. Un clic sur Cercle provoque la cration
dun cercle et son ajout la scne. La scne est de plus visualise en permanence sur
une zone de dessin de la fentre SCENE.

Un clic sur Cercle provoque, en (2), la cration et lajout dun cercle.


La cration dun cercle, en (5), est faite par un dialogue (moderne) au moyen dune
fentre CERCLE par laquelle loprateur fournit les coordonnes du centre et le
rayon, puis valide la saisie par OK.
Mais pour que cette programmation lgante soit possible, il est ncessaire que lactivit qui cre le cercle et lajoute la scne puisse attendre la fin de la saisie.
Or une telle activit, susceptible dattendre sans perdre son tat de contrle, cest-dire capable de repartir de l o elle en est, cest justement un processus. Dans cet
exemple, la situation dattente est en (4) par suite de lappel depuis (5) par suite de
lappel depuis (2) par suite de lappel depuis (1). Il est impratif que ce soit un processus diffrent du processus (unique) de gestion des vnements qui dordinaire se
charge de lexcution des procdures de raction aux vnements, sinon il pourrait
attendre longtemps car il attendrait dtre rveill par lui mme, puisque cest lui qui
est cens excuter la procdure de raction au bouton OK. Il faut donc que la raction
au bouton Cercle cre (ou active) un processus indpendant. Cest ce qui est fait en
(1) : la classe P est un modle de processus dont le programme principal appelle
cercle(). La procdure de raction actionPerformed cre et lance un tel processus.
Pour raliser lattente, on a utilis le dialogue du cercle comme un moniteur : en (4) la
cration du dialogue attend par wait() et en (3) le clic sur OK provoque le rveil par
notify().
60

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Paralllisme en Java : Threads

class Scene extends ListPrcr {


class FenetreDeScene extends Frame {
Button boutonCercle = new Button("Cercle");
class ActionCercle implements ActionListener {
private class P extends Thread { public void run() {cercle();} }
public synchronized void actionPerformed(ActionEvent e) {new P().start();}
}

Button boutonRedessine = new Button("Redessine");


class ActionRedessine implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {dessine();}
}
Button boutonQuitter = new Button("Quitter");
class ActionQuitter implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {System.exit(0);}
}
Panel dessin= new Panel(); // zone de dessin
public FenetreDeScene() { super("SCENE");
Placement.p(this,boutonCercle,
1,1,1,1);
Placement.p(this,boutonRedessine,1,2,1,1);
Placement.p(this,boutonQuitter, 1,3,1,1);
Placement.p(this,dessin,2,1,1,3,GridBagConstraints.CENTER,5,5,5,5,
10,10,GridBagConstraints.BOTH);
boutonCercle.addActionListener(new ActionCercle());
boutonRedessine.addActionListener(new ActionRedessine());
boutonQuitter.addActionListener(new ActionQuitter());
dessin.setBackground(Color.white); pack(); setVisible(true);
}
public void paint(Graphics g) {dessine();};
}
FenetreDeScene f = new FenetreDeScene();

2
public void cercle()

{ajoutEnFin(new Cercle()); dessine();}

public void dessine() {


Graphics g = f.dessin.getGraphics();; // graphique de dessin
g.clearRect(0,0,2000,1000);
debut();
while(! estEnFin()) {((Cercle) eltCourant()).dessine(g); avance();};
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

61

Paralllisme en Java : Threads

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);
}
}

public class TestControl {


static public void main(String argv[]) {new Scene();}
}

62

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Paralllisme en Java : Threads

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

Paralllisme en Java : Threads

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;

public String rechercheParallele(int nbPus, String cle) {


int quotaDeBase=nbAssoc/nbPus; int residu=nbAssoc%nbPus;
resul= new Resul(nbPus);
3
int debut=0;
for (int i=1; i<=nbPus; i++) {
int fin = debut + quotaDeBase + (residu>0?1:0);
new PusRecherche(debut,fin,cle).start();
4
residu--; debut=fin;
};
return (resul.attend());
5
}
}

64

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Paralllisme en Java : Threads

public class CalculPara {


public static void main (String argv[]) {
TableAssoc mineralogique = new TableAssoc(1000);
mineralogique.associer("1234 BZ 35", "toto");
mineralogique.associer("1235 BZ 35", "tata");
mineralogique.associer("1236 BZ 35", "titi");
mineralogique.associer("1237 BZ 35", "jojo");
mineralogique.associer("1238 BZ 35", "herve");
mineralogique.associer("1239 BZ 35", "jacques");
mineralogique.associer("1240 BZ 35", "louis");
mineralogique.associer("1241 BZ 35", "gerard");
mineralogique.associer("1242 BZ 35", "jean");
mineralogique.associer("1243 BZ 35", "simon");
mineralogique.associer("1244 BZ 35", "jules");
System.out.println (mineralogique.rechercheParallele(3,"1241 BZ 35"));
System.out.println (mineralogique.rechercheParallele(3,"3456 BZ 44"));
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

65

Documents HTML actifs : Applet

Documents HTML actifs : Applet

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.

class Applet extends Panel {


mthodes dfinir
void
void
void
void
void

init();
start();
paint(Graphic g);
stop();
destroy();

mthodes outils ( utiliser)


URL getCodeBase();
URL getDocumentBase();
AudioClip getAudioClip(URL urlAudioClip);
AudioClip getAudioClip(URL repertoire, String nom);
Image getImage(URL urlImage);
Image getImage(URL repertoire, String nom);
...
}
La programmation dune applet se fait en crivant les mthodes init(), start(),
paint(), stop() et destroy().
La mthode init() est appele juste aprs la cration de lapplet et permet de raliser les initialisations.
La mthode start() sert programmer les traitements, gnralement au moyen
dun processus spar, en crant un Thread.
Une applet possde automatiquement, par hritage, un Panel qui permet des affichages graphiques au sein du document HTML.
La mthode paint(), sollicite chaque fois que le Panel est dcouvert, permet de
programmer les effets visuels. Elle reoit en paramtre un contexte graphique g, objet
de classe Graphics, sur lequel on peut appliquer des oprations de dessin.
Une mthode de la classe Panel, repaint(), sans paramtre, peut tre appele,
par exemple depuis la mthode start(), pour forcer le rafrachissement de la zone

66

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Documents HTML actifs : Applet

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.

Voici quelques mthodes de dessin offertes par la classe Graphics :


class Graphics {
void
void
void
void

drawLine(int x1,int y1,int x2,int y2)dessine un segment


drawRect(int x,int y,int w,int h)
dessine un rectangle
drawOval(int x,int y,int w,int h)
dessine une ellipse
drawString(String s, int x,int y)
dessine un texte

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.
...
}

Linsertion de lapplet dans le document HTML se fait au moyen de balises :


<APPLET CODE=xxx.class WIDTH=250 HEIGHT=150> </APPLET>
La rubrique CODE indique le nom du fichier .class contenant lapplet. WIDTH et
HEIGHT fixent la largeur et la hauteur, en pixels, de la zone graphique au sein du
document.

Lexemple suivant illustre une applet qui affiche un petit chronomtre qui sincrmente chaque seconde :

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

67

Documents HTML actifs : Applet

import java.applet.*; import java.awt.*;


public class Chrono extends Applet {
int date;
class IncrementDate extends Thread {
public void run() {
while (true) {
try {sleep(1000);} catch(InterruptedException e) {}
date++; Applet.this.repaint();
}
}
}
public void init () {date = 0;}
public void start () {new IncrementDate().start();}
public void paint (Graphics g) {
setBackground(Color.yellow); g.drawRect(20,20,40,20);
g.drawString(String.valueOf(date),30,35);
}
}
Un document HTML activant cette applet pourrait tre :
<HTML>
<HEAD> <TITLE> CHRONO </TITLE> </HEAD>
<BODY>
<P> Ci-dessous tourne un petit chronomtre </P>
<APPLET CODE= Chrono.class WIDTH=100 HEIGHT=50> </APPLET>
</BODY>
</HTML>
La visualisation du document (avec le navigateur hotjava par exemple) donne :

68

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Documents HTML actifs : Applet

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.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

69

Communications rseau : net

10 Communications rseau : net


Le paquetage java.net offre les moyens de communication entre machines travers les rseaux, et notamment travers Internet. Il offre :
la manipulation des adresses Internet grce la classe InetAddress,
les communications de bas niveau grce aux classes DatagramPacket et
DatagramSocket,
les communications en mode connect grce aux classes Socket et ServerSocket,
laccs de type fichier dsign par URL grce aux classes URL et URLConnection.

10.1 Adresses Internet


Les machines connectes sur Internet sont identifies par une adresse unique de 32
bits (en 1998). Ces adresses sont reprsentes en Java par la classe InetAddress :
class InetAddress {
static InetAddress getLocalHost()
throws UnknownHostException;;
static InetAddress getByName(String Machine)
throws UnknownHostException;
String getHostName();
}
La mthode getLocalHost() retourne ladresse de la machine locale (celle o
sexcute couramment le programme). La mthode getByName(String
Machine) retourne ladresse associe la chane de caractres Machine. Cette
chane peut tre soit la reprsentation dune adresse de 32 bits sous forme de 4 nombres compris entre 0 et 255, par exemple "131.254.52.9", soit un nom en clair
"poseidon.ifsic.univ-rennes1.fr". La mthode getHostName()
retourne le nom de la machine associe une adresse.

10.2 Communications de bas niveau


Il sagit de transmissions de paquets individuels, en mode non connect. Ces transmissions sont peu fiables : elles ne garantissent pas la rception des paquets et nassurent pas que lordre darrive soit le mme que lordre dmission.
Les paquets sont reprsents par des objets de classe DatagramPacket :
class DatagramPacket {
DatagramPacket(byte[] buf, int lng);
DatagramPacket(byte[] buf, InetAddress adr, int port);
InetAddress getAddress();
byte[] getData;
int getLength()
int getPort()
}
70

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Communications rseau : net

DatagramPacket(byte[] buf, int lng) : ce premier constructeur cre un


objet destin recevoir un paquet.
DatagramPacket(byte[] buf, InetAddress adr, int port) : ce
constructeur spcifie un paquet destin tre mis vers le port port du site dadresse
adr.

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 :

site client : Questionneur

site serveur : Repondeur

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.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

71

Communications rseau : net

import java.net.*; import es.*;


public class Questionneur {
public static void main(String argv[]){
try {
InetAddress adrRepondeur =
InetAddress.getByName("florence.irisa.fr");
int portRepondeur = 8080;
DatagramPacket paquetReponse =
new DatagramPacket(new byte[1000], 1000);
DatagramSocket socket = new DatagramSocket();
String question = Lecture.chaine("\n");
while (!question.equals(".")) {
// envoit la question
byte strb[]= question.getBytes();
byte sbuf[]=new byte[1000];
for (int i=0;i<strb.length;i++){sbuf[i]=strb[i];};
DatagramPacket paquetQuestion =
new DatagramPacket(sbuf,1000,adrRepondeur,portRepondeur);
socket.send(paquetQuestion);
// recoit la reponse
socket.receive(paquetReponse);
System.out.println(
new String(paquetReponse.getData()));
question = Lecture.chaine("\n");
}
}catch(Exception e){
System.out.println("ERREUR"); System.exit(0);
};
}
}

72

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Communications rseau : net

import java.net.*; import java.io.*; import es.*;

public class Repondeur {


int portRepondeur= 8080;
DatagramSocket socketClient;
byte buf[] = new byte[1000];
DatagramPacket paquetQuestion= new DatagramPacket(buf, buf.length);
InetAddress adrClient ;
int portClient;

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){};
}
}

public static void main(String[] a) { new Repondeur();}


}

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

10.3 Communications fiables en mode connect


La classe Socket dfinit un canal de transmission entre machines. Aprs avoir cr
deux objets de type Socket, un sur chacun des sites communicants, et aprs les avoir
connects, les communications se font au moyen de canaux dentres/sorties squentielles de type InputStream et OutputStream. Le protocole de communication
(TCP) garantit le respect de lordre.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

73

Communications rseau : net

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

Pour mettre et recevoir sur des objets de type respectivement OutputSream et


InputStream, on dispose des mthodes void writeChars(String s) et
char readChar().
74

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Communications rseau : net

Lexemple suivant est la programmation dun questionneur et dun rpondeur du


mme type que prcdemment.
public class Repondeur {
public static void main(String arg[]) {
try{
ServerSocket accesRepondeur = new ServerSocket(8082);
Socket socketClient = accesRepondeur.accept();
DataInputStream inClient =
new DataInputStream(socketClient.getInputStream());
DataOutputStream outClient =
new DataOutputStream(socketClient.getOutputStream());
while (true) {
String question="";
char c=inClient.readChar();
while(c!='.'){question=question+c; c=inClient.readChar();};
System.out.println(question);
String reponse = Lecture.chaine("\n");
outClient.writeChars(reponse + '.');
}
}
catch(Exception e){System.out.println("ERREUR"); System.exit(0);}
}
}

public class Questionneur {


public static void main(String arg[]) {
try{
Socket socketServeur = new Socket("florence.irisa.fr",8082);
DataInputStream inServeur=
new DataInputStream(socketServeur.getInputStream());
DataOutputStream outServeur =
new DataOutputStream(socketServeur.getOutputStream());
while (true) {
String question = Lecture.chaine("\n");
outServeur.writeChars(question + '.');
String reponse="";
char c=inServeur.readChar();
while(c!='.'){reponse=reponse+c; c=inServeur.readChar();};
System.out.println(reponse);
}
}
catch(Exception e){System.out.println("ERREUR"); System.exit(0);};
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

75

Communications rseau : net

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Communications rseau : net

10.4 Accs par URL


La classe URL permet de faire apparatre comme un fichier squentiel de type
InputStream nimporte quelle ressource accessible sur le Web au moyen dune
URL.

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 :

import es.*; import java.net.*; import java.io.*;


public class TestUrl {
public static void main(String arg[]) {
System.out.println("url :");
String chaineUrl = Lecture.chaine("\n");
try{
URL url = new URL(chaineUrl);
InputStream in = url.openStream();
int k= in.read();
while (k!=-1) { System.out.print((char) k); k= in.read(); }
}
catch(EOFException e) {System.out.println("\ntermine");}
catch(Exception e) {System.out.println("erreur"); System.exit(0);};
}
}

La mthode openConnection() rend un objet de type URLConnection, que


nous ne dcrirons pas ici, qui permet des accs plus labors lURL.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

77

Applications rparties : rmi

11 Applications rparties : rmi


Le moyen le plus lgant de construire une application rpartie est offert par le paquetage rmi (Remote Method Invocation). Il permet, au prix dun effort minime, de
dfinir des objets distants (remote objects). Un tel objet peut rsider sur un site et
tre utilis depuis un autre site, en appelant ses mthodes comme si lobjet tait local,
avec juste quelques petites restrictions.

11.1 Mcanisme illustr sur un exemple


Nous prendrons lexemple simple suivant pour illustrer le mcanisme et les moyens
de lexprimer en Java.
Un site serveur dfinit un objet Calculateur dot dune mthode somme() qui
additionne deux entiers passs en paramtre et rend le rsultat sous forme dune
chane de caractres.
Sur un site client on connat lexistence de cet objet. Pour cet exemple, le site client
connat lobjet par une adresse externe qui est une chane de caractres forme du
nom du serveur, dun numro de port, et dun identificateur dobjet, de la forme :
//e103c04.ifsic.univ-rennes1.fr:8090/Calculateur
Le client obtient la rfrence de cet objet puis lutilise directement en appelant la
mthode somme().

site serveur

site client

Calculateur
...
somme(12,34)
...

"rsultat:46"

somme(int x,int y)

e103c04.ifsic.univ-rennes1.fr:8090

Pour raliser cela il faut procder comme suit :

1/ Dfinition de linterface dun objet distant


Le profil des mthodes offertes par un objet distant doit tre dfini au moyen dune
interface qui hrite de Remote :
import java.rmi.*;
public interface Calculateur extends Remote {
public String somme(int x,int y) throws RemoteException;

}
78

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Applications rparties : rmi

2/ Dfinition dune classe qui implmente un type dobjet distant


Le modle des objets de type Calculateur rsidant effectivement sur un site serveur doit tre implment au moyen dune classe, appele ici ServeurCalculateur, qui doit hriter de UnicastRemoteObject, classe de base dune catgorie
(la plus simple, il en existe dautres) dobjets capables dtre rfrencs distance.
Cette classe doit galement implmenter linterface Calculateur, cest dire
offrir les mthodes qui y sont dfinies :
import java.rmi.*;
import java.rmi.server.*; import java.rmi.registry.*;
class ServeurCalculateur extends UnicastRemoteObject
implements Calculateur {
public String somme(int x, int y) { return ("resultat : " + (x+y)); }
}

3/ Initialisation du site serveur,


enregistrement de ladresse externe dun objet distant
Le programme de test suivant, excut sur le site serveur, a pour but de crer un objet
ServeurCalculateur et de lassocier une adresse externe afin que le monde extrieur puisse le dsigner.
Il y a quelques petites tracasseries ce niveau : pour des raisons obscures de scurit
(obscures mais sans doute ncessaires), un site serveur dobjets distants, cest--dire
un site qui possde des objets accessibles par lextrieur, doit activer un gestionnaire
de scurit. Ceci peut tre fait au moyen de lincantation magique :
System.setSecurityManager(new RMISecurityManager())

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)

Lassociation entre ladresse externe //e103c04.ifsic.univ-rennes1.fr:8090/


Calculateur et un objet distant (gaston dans cet exemple) est faite par
Naming.bind().
class TestServeurClaculateur {
public static void main(String argv[]) {
System.setSecurityManager(new RMISecurityManager());
try {
LocateRegistry.createRegistry(8090);
ServeurCalculateur gaston = new ServeurCalculateur();
Naming.bind(
"//e103c04.ifsic.univ-rennes1.fr:8090/Calculateur",
gaston);
}
catch(Exception e) { System.out.println("erreur"); return;};
}}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

79

Applications rparties : rmi

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);
}
}
}

4/ Compilation des souches et des squelettes (stubs & skeletons)


Ce que nous venons de voir est suffisant en ce qui concerne les programmes sources.
Cependant le systme ncessite, pour chaque classe dobjet distant, deux classes supplmentaires, la souche (stub) et le squelette (skeleton).
Le squelette contient la logique qui soccupe de recevoir les paramtres et retourner le
rsultat du ct serveur. La souche contient la logique qui met les paramtres et rcupre les rsultats ct client.
client

serveur

stub

skeleton
communications rseau

80

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Applications rparties : rmi

Lexistence de ces classes est totalement transparente la programmation, mais cela


exige une compilation supplmentaire. Cette compilation se fait au moyen de la commande rmic (rmi compiler) avec pour argument le fichier .class de la classe qui
implmente des objets distants. Il faut donc linvoquer aprs compilation du source de
cette classe.
Dans notre exemple, aprs avoir excut :
javac ServeurCalculateur.java
on excute
rmic ServeurCalculateur
ce qui cre les deux fichiers :
ServeurCalculateur_Skel.class
ServeurCalculateur_Stub.class
Pour tester lexemple, il suffit alors de lancer le serveur sur le site prvu, savoir la
machine e103c04.ifsic.univ-rennes1.fr dans cet exemple, et de lancer
autant de clients que lon veut, sur nimporte quelles machines.

11.2 Communication de rfrences dobjets distants


Dans lexemple prcdent nous navons quun seul objet distant et cet objet est connu
par les clients grce une adresse externe, chane de caratres, sans doute communique de vive voix entre programmeurs ou entre utilisateurs humains.
Dans la moindre application un peu plus complique, la plupart des objets ne sont pas
dsigns ainsi. Lapplication cre dynamiquement des objets distants et leurs rfrences sont communiques en tant que paramtres ou rsultats dinvocations de mthodes.
Les objets distants, cest--dire de classe Remote, peuvent tre passs en paramtre
ou rendus en rsultat de mthodes, y compris de mthodes dobjets distants. Dans ce
cas, cest la rfrence lobjet qui est transmise, comme pour un objet local, la seule
diffrence tant que, de faon transparente, lors des transmissions entre sites, ces rfrences sont transmises sous une forme adquate, sans doute compose de ladresse du
site qui hberge lobjet et dune rfrence interne ce site.

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.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

81

Applications rparties : rmi

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Applications rparties : rmi

11.3 Passage des paramtres et rendu de rsultat


Lorsque un paramtre ou un rsultat de mthode dobjet distant est dun type de base
(scalaire), le passage se fait par valeur.
Lorsque un paramtre ou un rsultat de mthode dobjet distant est dun type classe
dobjet distant (driv de Remote), cest la rfrence lobjet qui est transmise.
En revanche si le paramtre ou le rsultat est dun type classe non distant, cest, sous
certaines conditions, un clone de lobjet qui est transmis : la valeur de lobjet est transmise, selon un format particulier standardis, et lautre extrmit un nouvel objet est
cr avec les valeurs reues. Cette cration est un peu particulire dans la mesure o
elle ne sollicite aucun constructeur.
Pour que le clonage soit possible, il faut cependant que lobjet transmis implmente
linterface Serializable. Cette interface est en fait une interface vide (il ny a
rien rdiger de faon standard). Il suffit simplement que la classe des objets que lon
souhaite ainsi transmettre soit dclare :
class UnObjetTransmissible implements Serializable { ...}
Le clonage ralis est remarquablement astucieux : si on a un rseau dobjets
Serializable qui se rfrencent mutuellement, tout le graphe dobjets connexe
lobjet pass en paramtre ou rendu en rsultat est transmis, en respectant la structure du graphe, et cela fonctionne mme si le graphe a des boucles.

return(

titre dexercice amusant, le lecteur pourra essayer de transmettre larbre gnalogique de lexercice 4 et vrifier que cela fonctionne.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

83

Sujets de travaux pratiques

12

84

Sujets de travaux pratiques

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Sujets de travaux pratiques

TP1
Gestion des tudiants

Programmer la gestion des tudiants propose dans lexercice 2

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.

Rdiger un programme principal qui gre les notes de mathmatiques, de franais et


danglais. Ce programme devra permettre dintroduire un nouvel tudiant, de modifier
son adresse, dajouter une note dtudiant une matire, dobtenir la moyenne des
notes dune matire.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

85

Sujets de travaux pratiques

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-

ments de la matrice qui napparaissent pas dans la liste tant nuls.


La seconde reprsentation est mieux adapte dans le cas de matrices creuses.

Le type matrice devra comporter au moins les mthodes suivantes :


addition dune autre matrice la matrice courante,
multiplication de la matrice courante par une autre.

Ne pas hsiter rajouter dautres fonctionnalits qui semblent judicieuses.

Rdiger un programme de test avec des matrices de taille 3 :


MT1, MT2 et MT3 selon la mise en uvre tableau,
ML1, ML2 et ML3 selon la mise en uvre liste.
Ce programme devra tester les oprations suivantes :
ML1:= ML1*ML2
ML2:= ML2*MT2
MT3:= MT3*ML3

86

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Sujets de travaux pratiques

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

Un fil est porteur dune valeur logique 0, 1 ou X, X reprsentant la valeur indtermine.


Dans le simulateur, ces valeurs seront reprsentes par les caractres 0, 1 et
X.
Initialement, les fils portent une valeur indtermine (X).

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

87

Sujets de travaux pratiques

Principe de fonctionnement du simulateur


Le but du simulateur est de faire fonctionner les circuits par instants successifs (unit
de temps simul), ou tapes. Chaque tape comporte deux phases :
Premire phase :
Chaque circuit de base possde une mthode dvaluation, appele topEval(), qui
dfinit son comportement. Le simulateur appelle les mthodes topEval() de tous
les circuits de base, ce qui a pour effet dattribuer une valeur future aux fils de sortie
en fonction des valeurs courantes portes par les fils dentre (ceci ne modifie pas la
valeur courante du fil).
Deuxime phase :
Chaque fil possde une mthode appele actualise() qui met jour sa valeur
courante au moyen de sa valeur future. Le simulateur appelle, pour chaque fil, sa
mthode actualise().

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Sujets de travaux pratiques

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.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

89

Sujets de travaux pratiques

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

Sortie des donnes tries


60

14

27

60

18

60

27

60

12

18

12

14

27

14

18

18

27

60

27

60

1 - Programmer cet algorithme parallle au moyen dun processus par emplacement,


en utilisant des moniteurs tampons une place pour le transfert dinformation entre
emplacements voisins. On prendra un nombre fixe demplacements (une constante
identifie par N).

90

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Sujets de travaux pratiques

2 - Bien que conceptuellement intressant comme exercice de programmation, ce tri


parallle noffre que peu dintrt avec un langage de programmation comme Java qui
nest pas vraiment adapt au calcul parallle. Cependant on peut lui trouver un intrt en tant que simulateur pour voir fonctionner un algorithme parallle. Cest ce
quon se propose de faire maintenant.

Modifier le programme prcdent pour visualiser le comportement de lagorithme.


chaque emplacement est associe la visualisation de ses registres min et max. Les
valeurs tries viennent sinscrire dans une zone de texte gauche.
Pour pouvoir suivre la visualisation anime du fonctionnement, il faut que chaque
emplacement visualise ses registres pendant au moins 0.5 secondes avant et aprs
dcision dchanger ou non min et max.

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

91

Sujets de travaux pratiques

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.

trois plus quatre

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Sujets de travaux pratiques

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

93

Sujets de travaux pratiques

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

Chaque client dialoguera au moyen dune fentre affichant question et rponse. Un


bouton supplmentaire FIN permettra de clore la session de dialogue de ce client avec
le serveur. La clture de session devra faire disparatre la fentre correspondante sur le
site serveur.
On pourra prvoir larrt du serveur au moyen dun bouton ARRET SERVEUR.

Transformer la partie cliente de cette application rpartie en une applet de faon


pouvoir poser des questions au site serveur au moyen dun navigateur.

94

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES
et de
TRAVAUX PRATIQUES

mars 1999

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

95

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig 1.

public - private - Classes internes

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig 2.

Classes - public - private

public class ListAssoc {


private class Maillon {
String cle; String valeur;
Maillon suivant;
Maillon(String c, String v, Maillon s) { cle=c; valeur=v; suivant=s;}
}
private Maillon tete;
public
public
public
tete
}

ListAssoc() {};
boolean estVide() {return tete==null;}
void ajoute(String c1, String c2) {
= new Maillon(c1,c2,tete);

public String cherche(String cle) {


Maillon courant=tete;
while (courant!=null) {
if (courant.cle.equals(cle)) {return courant.valeur;}
else {courant=courant.suivant;}
};
return null;
}
}

public class TestListAssoc {


public static void main(String argv[]) {
ListAssoc l = new ListAssoc();
l.ajoute("dupont","12");
l.ajoute("durand","19");
l.ajoute("machin","2");
System.out.println("la note de dupont est " + l.cherche("dupont"));
System.out.println("la note de durand est " + l.cherche("durand"));
System.out.println("la note de machin est " + l.cherche("machin"));
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

97

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig 3

Gestion des tudiants

Voir corrig du TP1

98

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

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();
};
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

99

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig 5

Hritage : compatibilit de types

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Hritage : test de classe


import list.*;

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 ListDocuments extends ListPrcr { // ListPrcr d'objet de type Document


void ajoute(Document d) { super.ajoutEnFin(d);}
Document elementCourant() {return (Document) eltCourant();}
}

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

Livre("Le pere Goriot", "Balzac", 458));


Livre("Nounours", "Chantal Goya", 14));
Dictionnaire("Larousse", 4500));
Livre("Tintin", "Herge", 62));
Dictionnaire("Petit Robert", 5000));

int nbLivres=0; int nbDicos=0;


bibli.debut();
while(!bibli.estEnFin()) {
if (bibli.elementCourant() instanceof Livre) {nbLivres++;}
else if (bibli.elementCourant() instanceof Dictionnaire) {nbDicos++;}
bibli.avance();
};
System.out.println("nombre de livres = " + nbLivres);
System.out.println("nombre de dictionnaires = " + nbDicos);
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

101

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig 6

Hritage - mthodes virtuelles

class A { void p() {System.out.print(" A ");} }


class B extends A { void p() {System.out.print(" B ");} }
class TestVirtualite {
static void q(A a) {a.p();}
public static void main(String argv[]) {
A a= new A(); B b=new B();
a.p(); q(a);
a=b;
a.p(); q(a); q(b);
}
}

imprime
A

102

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig 7

Utilisation des mthodes virtuelles : multiples reprsentations

import es.*;

abstract
abstract
abstract
abstract
abstract

class ListeOrdonnee { // liste ordonnee d'entiers


void ajoute(int e);
int ieme(int i);
int nbElements();
ListeOrdonnee cloner(); // cree un exemplaire equivalent

void fusion(ListeOrdonnee l){


ListeOrdonnee l2;
if(l!=this) {l2=l;} else {l2= cloner();};
for (int i=0; i<l2.nbElements(); i++) {ajoute(l2.ieme(i));};
}
void saisir() {
System.out.print("nombre d'elements : "); int taille=Lecture.unEntier();
System.out.print("elements : ");
for (int i=0; i<taille; i++) {ajoute(Lecture.unEntier());};
}
void editer() {
for (int i=0; i<nbElements(); i++) {System.out.print(" "+ieme(i));};
}
}

class ListeOrdonneeTable extends ListeOrdonnee {


private int T[]; private int taille;
ListeOrdonneeTable(int tailleMax) {T=new int[tailleMax]; taille=0;}
void ajoute(int e) {
if (taille<T.length) {// ne fait rien si taille max atteinte
int ouInserer=0;
while (ouInserer<taille && T[ouInserer]<e){ ouInserer++;};
// ouInserer = indice du 1er element >= e ou au dela du dernier
for(int i=taille; i>ouInserer; i--) {T[i]=T[i-1];};
T[ouInserer]=e; taille++;
}
}
int ieme(int i) {return T[i];}
int nbElements() {return taille;}
ListeOrdonnee cloner() {
ListeOrdonneeTable l = new ListeOrdonneeTable(T.length);
for(int i=0; i<taille; i++) {l.T[i]=T[i];}; l.taille=taille;
return l;
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

103

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

class ListeOrdonneeChainee extends ListeOrdonnee {


private class Maillon { int e; Maillon suivant;
Maillon(int i, Maillon s) {e=i; suivant=s;}
}
private Maillon tete; private int taille;
ListeOrdonneeChainee() {tete=null; taille=0;}
void ajoute(int e) {
Maillon ouInserer=null;
Maillon courant=tete;
while
(courant!=null
&&
courant.e<e){ouInserer=courant;
courant=courant.suivant;};
if (ouInserer==null) { /* cas particulier */ tete = new Maillon(e,tete);}
else { ouInserer.suivant = new Maillon(e,ouInserer.suivant);};
taille++;
}
int ieme(int i) {
Maillon courant=tete;
for(int k=0; k<i; k++) {courant=courant.suivant;};
return courant.e;
}
int nbElements() {return taille;}
ListeOrdonnee cloner() {
ListeOrdonneeChainee l = new ListeOrdonneeChainee();
if (taille!=0) {
Maillon courant= tete; Maillon ouChainer=null;
l.tete=new Maillon(courant.e,null);
ouChainer=l.tete; courant=courant.suivant;
for(int i=1; i<taille; i++) {
ouChainer.suivant=new Maillon(courant.e,null);
ouChainer=ouChainer.suivant; courant=courant.suivant;
};
};
l.taille=taille;
return l;
}
}

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();

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

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);
};
}}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

105

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

class OpeNumerique implements Ope {


private static final Int zero = new Int(0);
public Object neutre() {return zero;}
public Object mul(Object x, Object y) {
return new Int(((Int) x).val * ((Int) y).val);
}
public Object add(Object x, Object y) {
return new Int(((Int) x).val + ((Int) y).val);
}
public Object saisir() {
return new Int(Lecture.unEntier());
}
public void imprimer(Object x) {
System.out.print(((Int) x).val);
}}

class OpeWarshall implements Ope {


private static final int INFINI = 999;
private static final Int neutre = new Int(INFINI);
public Object neutre() {return neutre;}
public Object mul(Object x, Object y) {
if (((Int) x)==neutre || ((Int) y)==neutre ) { return neutre;}
else {return new Int(((Int) x).val + ((Int) y).val);}
}
public Object add(Object x, Object y) {
if (((Int) x).val < ((Int) y).val) {return x;}
else {return y;}
}
public Object saisir() {
int v = Lecture.unEntier();
if (v==INFINI) {return neutre;}
else {return new Int(v);}
}
public void imprimer(Object x) {
if ((Int) x == neutre) {System.out.print("-");}
else {System.out.print(((Int) x).val);}
}}
class TestMatriceGen {
static public void main (String [] argv) {
System.out.println("test numerique");
Ope numerique = new OpeNumerique();
Matrice M1 = new Matrice(3);
Matrice M2 = new Matrice(3);
System.out.println("\nMatrice 3x3 M1 :");
M1.saisir(numerique);
System.out.println("\nMatrice 3x3 M2 :");
M2.saisir(numerique);
System.out.println("M1 x M2 :\n");
Matrice.produit(M1,M2,numerique).imprimer(numerique);
Programmation par objets en Java
IFSIC - Universit de Rennes 1 - 1999

107

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig 10

Processus - moniteurs

class MoniteurProdCons {
String tampon; boolean estVide=true;

// Version incorrecte pour


// plusieurs producteurs ou consommateurs

synchronized void prod(String m,String nom) {


if(!estVide){ System.out.println("PRODUCTEUR "+nom+" ATTEND");
try {wait();} catch(InterruptedException e) {};
};
System.out.println(nom+" PRODUIT
: " + m);
tampon=m; estVide=false; notify();
}
synchronized String cons(String nom) {
if(estVide){ System.out.println("CONSOMMATEUR "+nom+" ATTEND");
try {wait();} catch(InterruptedException e) {};
};
System.out.println(nom+" CONSOMME : " + tampon);
String resul=tampon; estVide=true; notify(); return resul;
}}
class Producteur extends Thread {
MoniteurProdCons tampon; String nom;
public Producteur (MoniteurProdCons t, String n) { tampon=t; nom=n;}
public void run() {
tampon.prod("message1 de "+nom, nom); tampon.prod("message2 de "+nom,nom);
}}
class Consommateur extends Thread {
MoniteurProdCons tampon; String nom;
public Consommateur(MoniteurProdCons t, String
public void run() {
try {sleep(1000);} catch(InterruptedException
try {sleep(1000);} catch(InterruptedException
try {sleep(1000);} catch(InterruptedException
try {sleep(1000);} catch(InterruptedException
}}

n) { tampon=t; nom=n;}
e)
e)
e)
e)

{};
{};
{};
{};

tampon.cons(nom);
tampon.cons(nom);
tampon.cons(nom);
tampon.cons(nom);

public class TestProdCons {


static public void main(String argv[]) {
MoniteurProdCons tampon = new MoniteurProdCons();
new Producteur(tampon, "p1").start();
new Consommateur(tampon, "c1").start();
new Producteur(tampon, "p2").start();
}
}

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

cette production reveille a tort p2


message2 de p1 est perdu

de p2
de p2
de p2
blocage ...
Programmation par objets en Java

IFSIC - Universit de Rennes 1 - 1999

109

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

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;
}}

class Producteur extends Thread {


MoniteurProdCons tampon; String nom;
public Producteur (MoniteurProdCons t, String n) { tampon=t; nom=n;}
public void run() {
tampon.prod("message1 de "+nom, nom); tampon.prod("message2 de "+nom,nom);
}}
class Consommateur extends Thread {
MoniteurProdCons tampon; String nom;
public Consommateur(MoniteurProdCons t, String
public void run() {
try {sleep(1000);} catch(InterruptedException
try {sleep(1000);} catch(InterruptedException
try {sleep(1000);} catch(InterruptedException
try {sleep(1000);} catch(InterruptedException
}}

n) { tampon=t; nom=n;}
e)
e)
e)
e)

{};
{};
{};
{};

tampon.cons(nom);
tampon.cons(nom);
tampon.cons(nom);
tampon.cons(nom);

public class TestProdConsV1 {


static public void main(String argv[]) {
MoniteurProdCons tampon = new MoniteurProdCons();
new Producteur(tampon, "p1").start();
new Consommateur(tampon, "c1").start();
new Producteur(tampon, "p2").start();
}}

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig 11

Applets

Version 1 : cration de processus animation chaque dcouverte du document, arrt


par stop() lorsque le document est cach.
import java.applet.*; import java.awt.*;
public class Boing extends Applet {
String etat;
AudioClip audio1;
Animation anim;
class Animation extends Thread {
public void run() {
while (true) {
try {sleep(500);} catch (InterruptedException e) {}
if (etat.equals("")) {etat="BOING";
audio1.play();
}
else {etat="";}
Applet.this.repaint();
}
}
}
public void init () {etat = "";
audio1=getAudioClip(getDocumentBase(),"boing.au");
}
public void start () {
anim = new Animation();
anim.start();
}
public void paint (Graphics g) {
g.drawString(etat,110,80);
if (etat.equals("")) {g.drawOval(30,60,200,30);}
else {g.drawOval(30,30,200,100);}
}
public void stop () { anim.stop();}
public void destroy () {System.out.println("applet detruite");}
}

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

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Version 2 : le processus unique danimation est relanc par notify() chaque


dcouverte du document et attend par wait() lorsque le document est cach.

import java.applet.*;
import java.awt.*;

public class Boing2 extends Applet {


String etat;
AudioClip audio1;
Questionneur q;
class Animation extends Thread {
public void run() {
while (true) {
testAttente();
try {sleep(500);} catch (InterruptedException e) {}
if (etat.equals("")) {etat="BOING";
audio1.play();
}
else {etat="";}
Applet.this.repaint();
}
}
}
synchronized void testAttente() {
while(attente) {try{wait();} catch(InterruptedException e){}
}
}
public void init () {etat = "";
audio1=getAudioClip(getDocumentBase(),"boing.au");
}
public synchronized void start () {
q=new Questionneur(); q.start();
}
public void paint (Graphics g) {
g.drawString(etat,110,80);
if (etat.equals("")) {g.drawOval(30,60,200,30);}
else {g.drawOval(30,30,200,100);}
}
public synchronized void stop () { attente=true;}
public void destroy () {System.out.println("applet detruite");}
}

112

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig 12

import java.net.*; import java.io.*; import es.*;

public class Repondeur {


DatagramSocket socketClient;
byte buf[] = new byte[100];
DatagramPacket paquetQuestion= new DatagramPacket(buf, buf.length);
InetAddress addresseClient ;
int portClient;
public Repondeur() {
try{ socketClient = new DatagramSocket(8080);}
catch(SocketException e){}
while (true) {
try{socketClient.receive(paquetQuestion);} catch(Exception e){return;};
addresseClient = paquetQuestion.getAddress();
portClient = paquetQuestion.getPort();
System.out.println(new String(paquetQuestion.getData()));
byte strb[]= Lecture.chaine("\n").getBytes();
byte sbuf[]=new byte[100];
for (int i=0;i<strb.length;i++){sbuf[i]=strb[i];};
DatagramPacket paquetReponse =
new DatagramPacket(sbuf,100,addresseClient,portClient);
try {socketClient.send(paquetReponse);} catch(Exception ex){};
}
}
public static void main(String[] a) { new Repondeur();}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

113

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig 13
Voir corrig du TP6

Corrig 14
Voir corrig du TP7

114

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig TP1

Gestion des tudiants

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

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

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();
}
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

117

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig TP2

Deux reprsentations des matrices

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));};

public void affecter (Matrice M) {


for (int i=0; i<N; i=i+1) {
for (int j=0; j<N; j=j+1) { affElt(i, j, M.elt(i,j));};
};
}
}

class MatriceTab extends Matrice {


private float[][] T= new float[N][N];
public MatriceTab() {};
public float elt(int i, int j){return T[i][j];}
public void affElt(int i, int j, float v){T[i][j]=v;}
public Matrice tempo() {return new MatriceTab();};
};
118

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

class MatriceList extends Matrice {


private class Maillon {int i; int j; float x; Maillon suivant;};
private Maillon tete=null;
private Maillon cherche(int i, int j) {
Maillon courant=tete;
while ((courant!=null) && !((courant.i==i)&&(courant.j==j)))
{ courant = courant.suivant; };
return courant;
}
public MatriceList() {}
public float elt(int i, int j) {
Maillon m = cherche(i,j);
if (m!=null) {return m.x;} else {return 0;}
}
public void affElt(int i, int j, float v) {
Maillon m=cherche(i,j);
if (m!=null) { if (v!=0) {m.x = v;}
else { // nouvelle valeur nulle : suppression du maillon
if (m==tete) {tete=tete.suivant;}
else {
Maillon prec=tete;
while (prec.suivant!=m) {prec=prec.suivant;};
prec.suivant=m.suivant;
}
}
}
else { if (v!=0) {
Maillon nouv= new Maillon();
nouv.i=i; nouv.j=j; nouv.x=v; nouv.suivant=tete;
tete=nouv;
}
}
}
public Matrice tempo() {return new MatriceList();}
}

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();
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

119

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig TP3

Simulateur de circuits logiques

import java.awt.*; import java.awt.event.*;


import es.*; import list.*; import ihm.*;
class Bit {
public static
switch (b1)
case '0'
case '1'
default
};
};

char et(char b1,char b2) {


{
: return '0';
: return b2;
: if(b2=='0') return '0'; else return 'X';

public static char non(char b) {


switch (b) {
case '0' : return '1';
case '1' : return '0';
default : return 'X';
};
};
}

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();
};

abstract class CircuitDeBase {


//
Circuit + procedure d'evaluation topEval
// + liste de tous les circuits de base, membre statique
public CircuitDeBase() {tous.ajoutEnFin(this);};
abstract public void topEval();
public static ListPrcr /* de CircuitDeBase*/ tous=new ListPrcr();
};

class ET extends CircuitDeBase {


private Fil e1; Fil e2; Fil s;
public ET (Fil ee1, Fil ee2, Fil st) {e1=ee1; e2=ee2; s=st;};
public void topEval() { s.ecrire(Bit.et(e1.lire(),e2.lire()));};
};

class INV extends CircuitDeBase {


private Fil e; Fil s;
public INV (Fil ee, Fil st) {e=ee; s=st;};
public void topEval() { s.ecrire(Bit.non(e.lire()));};
};

120

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

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 VERROU { // verrou SetReset


private NON_ET ne1; private INV i1; private NON_ET ne2; private INV i2;
private Fil f1=new Fil(); private Fil f2=new Fil(); private Fil fr2=new Fil();
public VERROU(Fil ma1, Fil ma0, Fil s) {
i1=new INV(ma1,f1); ne1=new NON_ET(f1,s,fr2);
i2=new INV(ma0,f2); ne2=new NON_ET(fr2,f2,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

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

class FenetreSimul extends Frame {


private Checkbox poussoirMa0 = new Checkbox("Mise a 0");
class Ma0 implements FournisseurDeBit {
public char lire() {if(poussoirMa0.getState()){return '1';} else {return '0';}}
}
private Checkbox poussoirMa1 = new Checkbox("Mise a 1");
class Ma1 implements FournisseurDeBit {
public char lire() {if (poussoirMa1.getState()) {return '1';} else {return
'0';};}
}
Button boutonTop = new Button("top");
class ActionTop implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {simulateur.top();}
}
TextField affichageMa0 = new TextField(40);
class AfficheMa0 implements AfficheurDeBit {
public void ecrire(char c) {affichageMa0.setText(affichageMa0.getText()+c);}
}
TextField affichageMa1 = new TextField(40);
class AfficheMa1 implements AfficheurDeBit {
public void ecrire(char c) {affichageMa1.setText(affichageMa1.getText()+c);}
}
TextField affichageS = new TextField(40);
class AfficheS implements AfficheurDeBit {
public void ecrire(char c) {affichageS.setText(affichageS.getText()+c);}
}
Button boutonQuit=
new Button("quit");
class ActionQuit implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {System.exit(0);}
}
SimulVerrou simulateur;
public FenetreSimul() {

super("CONTROLE DE LA SIMULATION D'UN VERROU");

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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

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;};
};
}}

Tamp1 tamponE = new Tamp1(); // tampon d'entree


Tamp1 tamponS = new Tamp1(); // tampon de sortie
class Bouchon extends Tamp1 { // modele de tampon de droite (tampon bidon)
void prod(int v) {}
int cons() {return infini;}
}
Tamp1 bouchon= new Bouchon();

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();

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

123

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

System.out.println("entrer "+2*N+" valeurs entieres");


for(int i=0; i<2*N; i++) { tamponE.prod(Lecture.unEntier());};
System.out.println("valeurs triees");
for(int i=0; i<2*N; i++) { System.out.print(tamponS.cons()+" ");};
System.out.println("");
}}
public class TriPara {static public void main(String argv[]){new Trieuse();}}

Version avec visualisation


import es.*; import ihm.*; import java.awt.*;
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 = 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

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

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;};
};
}}

minmax.afficher(min,max,500);
minmax.afficher(min,max,1000);

minmax.afficher(min,max,500);
minmax.afficher(min,max,1000);

Tamp1 tamponE = new Tamp1(); // tampon d'entree


Tamp1 tamponS = new Tamp1(); // tampon de sortie
class Bouchon extends Tamp1 { // modele de tampon de gauche (tampon bidon)
void prod(int v) {}
int cons() {return infini;}
}
Tamp1 bouchon= new Bouchon();
Frame fTrieuse = new Frame("TRIEUSE");

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);
}}

public class TriPara {


static public void main(String argv[]){ new Trieuse();}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

125

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig TP5

Applet pour apprendre compter

import java.applet.*; import java.awt.*; import java.awt.event.*;


import ihm.*; import java.util.*;
public class ApprendCompter extends Applet {
int etat;
static final int poseQuestion
static final int reponseFournie
static final int delaiEcoule
int iRep;
AudioClip
AudioClip
AudioClip
TextField

= 1;
= 2;
= 3;

nombre[]= new AudioClip[19];


plus; AudioClip non;
laReponse; AudioClip exact;
question = new TextField(10);

Button boutonRep[] = new Button[19];


class ActionRep implements ActionListener {
int i;
ActionRep(int i) {this.i=i;}
public synchronized void actionPerformed(ActionEvent e) {reponse(i);}
}
Questionneur q;
class Questionneur extends Thread {
Random gen= new Random();
public void run() {
while (true) {
int i=Math.abs(gen.nextInt())%10; int j=Math.abs(gen.nextInt())%10;
etat = poseQuestion;
question.setText("
"+i+" + "+j);
prononce(nombre[i],2000);prononce(plus,2000);prononce(nombre[j],100);
Chrono c = new Chrono(5000); c.start(); attendre();
switch (etat) {
case reponseFournie:
c.stop();
if (iRep==i+j) { prononce(exact,2000);}
else {
prononce(non,2000);prononce(laReponse,2000);
prononce(nombre[i+j],2000);
}
break;
case delaiEcoule:
prononce(laReponse,2000); prononce(nombre[i+j],2000);
break;
}}}}

class Chrono extends Thread {


int duree;
public Chrono(int d) {duree=d;}
public void run() {
try {sleep(duree);} catch (InterruptedException e) {}
delaiEcoule();
}
}
126

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

synchronized void reponse(int i) {


etat = reponseFournie; iRep=i; notify();
}
synchronized void delaiEcoule() {
etat=delaiEcoule; notify();
}
synchronized void attendre() {
try{wait();} catch(InterruptedException e){}
}
static void prononce(AudioClip audio, int duree) {
audio.play(); try {Thread.sleep(duree);} catch (InterruptedException e){}
}

public void init () {


setBackground(Color.yellow);
Font f = new Font("Time",Font.BOLD,18);
question.setEditable(false); question.setFont(f);
Placement.p(this,question,7,1,7,1);
for (int i=0; i<19; i++) {
boutonRep[i]=new Button(String.valueOf(i));
boutonRep[i].addActionListener(new ActionRep(i));
Placement.p(this,boutonRep[i],i,2,1,1);
nombre[i]=getAudioClip(getDocumentBase(),"n"+i+".au");
}
plus=getAudioClip(getDocumentBase(),"plus.au");
non=getAudioClip(getDocumentBase(),"non.au");
laReponse=getAudioClip(getDocumentBase(),"laReponse.au");
exact=getAudioClip(getDocumentBase(),"exact.au");}

public synchronized void start () {


q=new Questionneur(); q.start();
}
public synchronized void stop () { q.stop();}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

127

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig TP6

Socket - ServerSocket

import java.net.*; import java.io.*;


import java.awt.*; import java.awt.event.*; import ihm.*;
public class RepondeurMultiple {
public static void main(String arg[]) {
ServerSocket porte;
try {porte = new ServerSocket(8082);}
catch( Exception e) {System.out.println("erreur newServerSocket"); return;}
int i=1;
try { Thread.currentThread().setPriority(
Thread.currentThread().getPriority()-1);
} catch(Exception e) {};
while (true) {
try {new Service(porte.accept(),i); i++;}
catch( Exception e) {System.out.println("erreur ServerSocket");}
}
}
}

class Service extends Thread {


Frame f ;
TextField question = new TextField(40);
TextField reponse = new TextField(40);
Label labClient;
Label lq=new Label("Question : ");
Label lr=new Label("Reponse : ");
Socket client ;
DataInputStream in;
DataOutputStream out;
Button boutonRepond = new Button("REPOND");
class ActionRepond implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
try {out.writeChars(reponse.getText()+'.');} catch(Exception ex){};
}
}
public Service(Socket client, int k) {
f=new Frame("SERVICE"+k);
labClient = new Label("de " + client.getInetAddress().getHostName());
Placement.p(f,labClient,1,0,1,1);
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);
try{
in = new DataInputStream(client.getInputStream());
out = new DataOutputStream(client.getOutputStream());
} catch(Exception e){System.out.println("erreur streams");}
this.start();
128

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

public void run() {


while (true) {
String quest="";
try {
char c=in.readChar();
while(c!='.'){quest=quest+c; c=in.readChar();};
} catch(Exception e){ return;};
question.setText(quest);
}
}
}

import java.net.*; import java.io.*; import es.*;


public class Questionneur {
public static void main(String arg[]) {
try{
Socket socketServeur = new Socket("florence.irisa.fr",8082);
DataInputStream inServeur=
new DataInputStream(socketServeur.getInputStream());
DataOutputStream outServeur =
new DataOutputStream(socketServeur.getOutputStream());
while (true) {
String question = Lecture.chaine("\n");
outServeur.writeChars(question + '.');
String reponse="";
char c=inServeur.readChar();
while(c!='.'){reponse=reponse+c; c=inServeur.readChar();};
System.out.println(reponse);
}
}
catch(Exception e){System.out.println("ERREUR"); System.exit(0);};
}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

129

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Corrig TP7

RMI

Dfinition des interfaces des objets distants


Accueil des clients - ouverture dune session
import java.rmi.*;
public interface ServeurSession extends Remote {
public Repondeur ouvreSession() throws RemoteException;
}
Logique dune session
import java.rmi.*;
public interface Repondeur extends Remote {
public void poseQuestion(String q) throws RemoteException;
public String obtientReponse() throws RemoteException;
public void fin() throws RemoteException;
}

Implmentation des objets distants


import java.applet.*; import java.awt.*; import java.awt.event.*; import ihm.*;
import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*;
Serveur de sessions Repondeur - objet distant connu par un nom externe
public class ImplementServeurSession extends UnicastRemoteObject
implements ServeurSession {
static final int portServeur = 8686;
Frame f ;
Button boutonArret = new Button("ARRET DU SERVEUR");
class ActionArret implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
try { Naming.unbind(
"//lilliput.ifsic.univ-rennes1.fr:"+portServeur+"/ServeurSession");
} catch (Exception ex) {System.out.println("erreur unbind");};
System.exit(0);
}
}
public ImplementServeurSession() throws RemoteException {
f=new Frame("SERVEUR SESSION");
Placement.p(f,boutonArret,1,1,1,1);
boutonArret.addActionListener(new ActionArret());
f.pack(); f.setVisible(true);
}
public Repondeur ouvreSession() {
try { return new ImplementRepondeur();}
catch(Exception e) {
System.out.println("erreur creation repondeur"); return null;};
}
130

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

public static void main(String arg[]) {


System.setSecurityManager(new RMISecurityManager());
try {
LocateRegistry.createRegistry(portServeur);
ServeurSession gaston = new ImplementServeurSession();
Naming.bind(
"//lilliput.ifsic.univ-rennes1.fr:"+portServeur+"/ServeurSession",gaston);
System.out.println("ServeurSession Demarre");
}
catch(AlreadyBoundException e) {System.out.println("erreur AlreadyBound");
return;}
catch(AccessException e) {System.out.println("erreur Access"); return;}
catch(RemoteException e) {System.out.println("erreur Remote"); return;}
catch(Exception e) {System.out.println("erreur ???"); return;};
}
}

Service repondeur - cr dynamiquement pour chaque client


class ImplementRepondeur

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();}
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

131

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Site client : questionneur


import java.applet.*; import java.awt.*; import java.awt.event.*;
import ihm.*;import java.rmi.*;import java.rmi.registry.*;
public class Questionneur extends Frame {
TextField question = new TextField(15);
TextField reponse = new TextField(15);
Repondeur rep;
Button boutonEnvoi = new Button("ENVOI");
class ActionEnvoi implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
try {
rep.poseQuestion(question.getText());
reponse.setText(rep.obtientReponse());
} catch(Exception ex){};
}
}
Button boutonFin = new Button("FIN");
class ActionFin implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
try { rep.fin(); System.exit(0);} catch(Exception ex){};
}
}
public Questionneur(){
Placement.p(this,new Label("Question : "),0,1,1,1);
Placement.p(this,question,1,1,1,1);
Placement.p(this,boutonEnvoi,1,2,1,1);
Placement.p(this,boutonFin,2,2,1,1);
Placement.p(this,new Label("Reponse : "),0,3,1,1);
Placement.p(this,reponse,1,3,1,1);
boutonEnvoi.addActionListener(new ActionEnvoi());
boutonFin.addActionListener(new ActionFin());
pack(); setVisible(true);
try {
ServeurSession serv = (ServeurSession)
Naming.lookup("//florence.irisa.fr:8686/ServeurSession");
rep = serv.ouvreSession();
} catch(Exception e){System.out.println ("erreur");}
}
public static void main(String arg[]){ new Questionneur();}
}

132

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

CORRIGS dEXERCICES et de TRAVAUX PRATIQUES

Version Applet du questionneur


import java.applet.*; import java.awt.*; import java.awt.event.*;
import ihm.*; import java.rmi.*; import java.rmi.registry.*;
public class AppletQuestionneur extends Applet {
TextField question = new TextField(15);
TextField reponse = new TextField(15);
Button boutonEnvoi = new Button("ENVOI");
class ActionEnvoi implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
try {
rep.poseQuestion(question.getText());
reponse.setText(rep.obtientReponse());
} catch(Exception ex){};
}}
Repondeur rep; // Repondeur distant pour ce Questionneur
Button boutonFin = new Button("FIN");
class ActionFin implements ActionListener {
public synchronized void actionPerformed(ActionEvent e) {
try { rep.fin(); System.exit(0);} catch(Exception ex){};
}}
public void init(){
Placement.p(this,new Label("Question : "),0,1,1,1);
Placement.p(this,question,1,1,1,1);
Placement.p(this,boutonEnvoi,1,2,1,1);
Placement.p(this,boutonFin,2,2,1,1);
Placement.p(this,new Label("Reponse : "),0,3,1,1);
Placement.p(this,reponse,1,3,1,1);
boutonEnvoi.addActionListener(new ActionEnvoi());
boutonFin.addActionListener(new ActionFin());
try { // connexion au serveur de sessions, connu par son nom externe
ServeurSession serv = (ServeurSession)
Naming.lookup("//lilliput.ifsic.univ-rennes1.fr:8686/ServeurSession");
// creation du Repondeur distant, obtention d'une reference d'objet distant
rep = serv.ouvreSession();
} catch(Exception e){System.out.println ("erreur");}
}}

page HTML qui contient l'applet


//
//
//
//
//
//
//
//
//
//
//
//

<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

IFSIC - Universit de Rennes 1 - 1999

133

Paquetages utiliss dans les exercices

Paquetages utiliss dans les exercices


Entres clavier : es
package es;
import java.io.*;

public class Lecture {


public static char unCar() {
// lecture d'un caractere
char c;
try { c = (char) System.in.read();
} catch(IOException e) {c= (char) 0;};
return c;
}

public static char unCarCmde() {


// lecture d'un caractere et consommation jusqu'au 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 d'une chaine comprise entre delimiteurs
StringBuffer b = new StringBuffer();
char c=unCar();
// ignore les delimiteurs de tete
while (delimiteurs.indexOf(c)!=-1) {c=unCar();};
// lit jusqu'au prochain delimiteur
while (delimiteurs.indexOf(c)==-1) {b.append(c); c=unCar();};
return b.toString();
}

public static String chaine() {


// lecture d'une chaine comprise entre " ", ",", "." ou "\n"
return chaine(" ,.\n");
}

public static int unEntier() {


// lecture d'un entier represente en decimal
String s=Lecture.chaine(" ,.\n");
try { return Integer.parseInt(s);
} catch(NumberFormatException e) {return 0;};
}
}
134

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Paquetages utiliss dans les exercices

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); }
}

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

135

Paquetages utiliss dans les exercices

Fentrage - vnements : ihm


package ihm;
import java.awt.*;
// procedures de placement de composants visuels dans un receptacle
public class Placement {
static GridBagLayout placeur= new GridBagLayout();
static GridBagConstraints c = new GridBagConstraints();
// procedure generale de positionnement
//------------------------------------public static void p(
Container cont, Component comp, int x, int y, int w, int h, int pos,
int t, int l, int b, int r, double wx, double wy, int fill) {
cont.setLayout(placeur);
c.gridx=x; c.gridy=y; // position (en nbre de cellules) du coin nord-est
c.gridwidth=w; c.gridheight=h; // largeur et hauteur (en nbre de cellules)
c.fill=fill; // directions d'expansion : NONE, BOTH, HORIZONTAL, VERTICAL
c.anchor=pos; // position du composant dans ses cellules :
// CENTER, EAST,NORTHEAST, NORTH, NORTHWEST,
// WEST, SOUTHWEST, SOUTH, SOUTHEAST ...
c.weightx=wx; c.weighty=wy; // ponderation de la distribution de l'espace
// supplementaire en cas d'agrandissement
c.insets = new Insets(t,l,b,r); // marges en pixels
placeur.setConstraints(comp, c); cont.add(comp);
};
// placement d'un composant qui ne grossit pas
//-------------------------------------------public static void p(Container cont, Component comp,
int x, int y, int w, int h, int pos, int t, int l, int b, int r) {
p(cont, comp, x, y, w, h, pos, t, l, b, r,
0.0, 0.0, GridBagConstraints.NONE);
};
// positionnement d'un composant sans marges qui ne grossit pas
//------------------------------------------------------------public static void p(Container cont, Component comp,
int x, int y, int w, int h, int pos) {
p(cont, comp, x, y, w, h, pos,
0, 0, 0, 0, 1.0, 1.0,
GridBagConstraints.NONE);
};
// positionnement au centre d'un composant sans marges qui ne grossit pas
//----------------------------------------------------------------------public static void p(Container cont, Component comp,
int x, int y, int w, int h) {
p(cont, comp, x, y, w, h,
GridBagConstraints.CENTER, 0, 0, 0, 0, 1.0, 1.0,
GridBagConstraints.NONE);
};
}

136

Programmation par objets en Java


IFSIC - Universit de Rennes 1 - 1999

Vous aimerez peut-être aussi