Vous êtes sur la page 1sur 65

Java

Java est un langage de programmation dvelopp par Sun Microsystems en 1995. Il a russi intresser
beaucoup de dveloppeurs travers le monde. Et pourquoi donc ?
La rponse est vaste et forcment sujet polmiques. En voici les principaux avantages :

C'est un langage orient objet driv du C, mais plus simple que le C++.
Il est multi-plateforme : tous vos programmes tourneront sans modification sur toutes les
plateformes o existe Java.
Il est dot en standard d'une riche bibliothque de classes, comprenant la gestion des interfaces
graphiques (fentres, boites de dialogue, contrles, menus, graphisme), la programmation multithreads (multitches), la gestion des exceptions, les accs aux fichiers et au rseau (notamment
Internet),...

Les deux derniers points ont contribu utiliser d'abord ce langage pour dvelopper des applets, qui sont
des applications qui peuvent tre tlcharges via Internet et excutes dans un navigateur sur n'importe
quelle plateforme. Ainsi, une page statique HTML peut s'enrichir de programmes qui lui donneront un
comportement dynamique.

Comment installer Java sur ton ordinateur


Pour pouvoir programmer en Java, on a besoin du logiciel JDK (Java Development Kit).
Le JDK est disponible gratuitement sur le site Java de Sun Microsystems peut tre tlcharge depuis ce
site : http://java.sun.com/j2se.
Une fois que le JDK est install, il vous faudra en plus au moins un diteur de texte (n'importe lequel, peut
importe) pour crire des programmes Java, par exemple le Bloc-notes.

Principe de fonctionnement
Java est langage qui doit tre compil et interprt. Compil et interprt ? En fait dans une premire
phase, vous compilez un programme (un ou plusieurs fichiers source .java) en fichiers .class et le
compilateur gnre un fichier .class. L'ensemble des fichiers .class est ensuite interprt par la Machine
Virtuelle Java (Java Virtual Machine) pour excuter le programme.
Pourquoi ne pas interprter directement le programme Java ?
1. Les fichiers .class contiennent du bytecode, une sorte de code machine Java (comparable au code
machine d'un microprocesseur), qui sera excut beaucoup plus rapidement par l'interprteur.
2. Seuls les fichiers .class sont ncessaires l'excution d'un programme Java. Comme ils
contiennent du code machine, ils ne peuvent tre lus.
3. tant compils, les fichiers .class sont de taille plus petite que le code source Java ; ceci est un
argument important pour transfrer les programmes sur Internet.
4. Chaque fichier .class dcrivant une classe d'objet, une mme classe peut tre utilis par diffrents
programmes, sans que cette classe ne soit duplique dans chacun des programmes.
Structure d'un programme
La structure d'un programme Java est plus simple qu'en C.
1

Chaque fichier qui le compose, se divise en trois parties (optionnelles) :


/* Dbut du fichier NouvelleClasse.java */
/* 1. Une ventuelle dclaration de package */
package nomPackage;
/* 2. Zro ou plusieurs import */
import nomClasse;
import nomPackage.nomClasse;
import nomPackage.*;

// Importer une classe sans package


// Importer une classe d'un package
// Importer toutes les classes d'un package

/* 3. Dclarations des classes et des interfaces du fichier */


public class NouvelleClasse
// Une seule classe ou interface dclare public,
{
// et par convention qui porte le mme nom que le fichier
// Corps de NouvelleClasse
}
class NouvelleClasse2
{
// Corps de NouvelleClasse2
}
interface NouvelleInterface
{
// Corps de NouvelleInterface
}
// ...
/* Fin du fichier NouvelleClasse.java */

Les packages sont comparables des sous-rpertoires.


Dans un programme on trouve deux choses :
- des donnes
- les instructions qui les manipulent
Java utilise les types de donnes suivants :
les nombres entiers
les nombres rels
les caractres et chanes de caractres
les boolens
les objets
Dclaration des donnes
La syntaxe de dclaration d'une constante est la suivante :
final type nom=valeur;
//dfinit constante nom=valeur
ex: final float PI=3.141592F;
Les conversions entre nombres et chanes de caractres
nombre -> chane
"" + nombre
chaine -> int
Integer.parseInt(chaine)
chane -> long
Long.parseLong(chaine)
chane -> double
Double.valueOf(chaine).doubleValue()
2

chane -> float

Float.valueOf(chaine).floatValue()

La conversion d'une chane vers un nombre peut chouer si la chane ne reprsente pas un nombre valide.
Il y a alors gnration d'une erreur fatale appele exception en Java. Cette erreur peut tre gre par la
clause try/catch suivante :
try{
appel de la fonction susceptible de gnrer l'exception
} catch (Exception e){
traiter l'exception e
}
instruction suivante

Les tableaux de donnes


Un tableau Java est un objet permettant de rassembler sous un mme identificateur des donnes de mme
type. Sa dclaration est la suivante :
Type Tableau[]=new Type[n] ou Type[] Tableau=new Type[n]
Les deux syntaxes sont lgales. n est le nombre de donnes que peut contenir le tableau. La syntaxe
Tableau[i] dsigne la donne n i o i appartient l'intervalle [0,n-1]. Toute rfrence la donne
Tableau[i] o i n'appartient pas l'intervalle [0,n-1] provoquera une exception.
Un tableau deux dimensions pourra tre dclar comme suit :
Type Tableau[][]=new Type[n][p] ou Type[][] Tableau=new Type[n][p]

Voici un exemple :
public class test1{
public static void main(String arg[]){
float[][] taux=new float[2][2];
taux[1][0]=0.24F;
taux[1][1]=0.33F;
System.out.println(taux[1].length);
System.out.println(taux[1][1]);
}
}

et les rsultats de l'excution :


2
0.33

Ecriture sur cran


La syntaxe de l'instruction d'criture sur l'cran est la suivante :
System.out.println(expression)
o expression est tout type de donne qui puisse tre converti en chane de caractres pour tre affich
l'cran.
Lecture de donnes tapes au clavier
Le flux de donnes provenant du clavier est dsign par l'objet System.in de type InputStream. Ce type
d'objets permet de lire des donnes caractre par caractre. C'est au programmeur de retrouver ensuite
3

dans ce flux de caractres les informations qui l'intressent. Le type InputStream ne permet pas de lire d'un
seul coup une ligne de texte. Le type BufferedReader le permet avec la mthode readLine.
Afin de pouvoir lire des lignes de texte tapes au clavier, on cre partir du flux d'entre System.in de type
InputStream, un autre flux d'entre de type BufferedReader cette fois :
BufferedReader IN=new BufferedReader(new InputStreamReader(System.in));

Nous n'expliquerons pas ici les dtails de cette instruction qui fait intervenir la notion de constructions
d'objets. Nous l'utiliserons telle-quelle.
La construction d'un flux peut chouer : une erreur fatale, appele exception en Java, est alors gnre. A
chaque fois qu'une mthode est susceptible de gnrer une exception, le compilateur Java exige qu'elle soit
gre par le programmeur. Aussi, pour crer le flux d'entre prcdent, il faudra en ralit crire :
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
System.err.println("Erreur " +e);
System.exit(1);
}

De nouveau, on ne cherchera pas expliquer ici la gestion des exceptions. Une fois le flux IN prcdent
construit, on peut lire une ligne de texte par l'instruction :
String ligne;
ligne=IN.readLine();

La ligne tape au clavier est range dans la variable ligne et peut ensuite tre exploite par le programme.
Expression arithmtique
Les oprateurs des expressions arithmtiques sont les suivants :
+
addition
soustraction
*
multiplication
/
division : le rsultat est le quotient exact si l'un au moins des oprandes est rel. Si les
deux oprandes sont entiers le rsultat est le quotient entier. Ainsi 5/2 -> 2 et 5.0/2 ->2.5.
%
division : le rsultat est le reste quelque soit la nature des oprandes, le quotient tant lui
entier. C'est donc l'opration modulo.
Il existe diverses fonctions mathmatiques :
double sqrt(double x)
racine carre
double cos(double x)
Cosinus
double sin(double x)
Sinus
double tan(double x)
Tangente
double pow(double x,double y) x la puissance y (x>0)
double exp(double x)
Exponentielle
double log(double x)
Logarithme nprien
double abs(double x)
valeur absolue
etc...
4

Toutes ces fonctions sont dfinies dans une classe Java appele Math. Lorsqu'on les utilise, il faut les
prfixer avec le nom de la classe o elles sont dfinies. Ainsi on crira :
double x, y=4;
x=Math.sqrt(y);

Priorits dans l'valuation des expressions arithmtiques


La priorit des oprateurs lors de l'valuation d'une expression arithmtique est la suivante (du plus
prioritaire au moins prioritaire) :
[fonctions], [ ( )],[ *, /, %], [+, -]
Les oprateurs d'un mme bloc [ ] ont mme priorit.
Expressions relationnelles
Les oprateurs sont les suivants : <, <=, ==, !=, >, >=
Ordre de priorit
>, >=, <, <=
==, !=
Le rsultat d'une expression relationnelle est le boolen false si expression est faussetrue sinon.
Exemple :
boolean fin;
int x;
fin=x>4;

Fonctions de comparaisons de deux chanes


On ne peut utiliser ici les oprateurs relationnels <, <=, ==, !=, >, >= . Il faut utiliser des mthodes de la
classe String :
String chaine1, chaine2;
chaine1=;
chaine2=;
int i=chaine1.compareTo(chaine2);
boolean egal=chaine1.equals(chaine2);

Ci-dessus, la variable i aura la valeur :


0 si les deux chanes sont gales
1 si chane n1 > chane n2
-1 si chane n1 < chane n2
La variable egal aura la valeur true si les deux chanes sont gales.
Expressions boolennes
Le rsultat d'une expression boolenne est un boolen.
ordre de priorit ! , &&, ||
Combinaison d'oprateurs
a=a+b peut s'crire a+=b
a=a-b peut s'crire a-=b
Il en est de mme avec les oprateurs /, %,* ,<<, >>, &, |, ^
Ainsi a=a+2; peut s'crire a+=2;

Oprateurs d'incrmentation et de dcrmentation


La notation variable++ signifie variable=variable+1 ou encore variable+=1
La notation variable-- signifie variable=variable-1 ou encore variable-=1.
L'oprateur ?
Exemple
i=(j>4 ? j+1:j-1);
affectera la variable i :
j+1 si j>4, j-1 sinon
C'est la mme chose que d'crire if(j>4) i=j+1; else i=j-1; mais c'est plus concis.
Les changements de type
Il est possible, dans une expression, de changer momentanment le codage d'une valeur. On appelle cela :
changer le type d'une donne ou en anglais type casting. La syntaxe du changement du type d'une valeur
dans une expression est la suivante (type) valeur.
La valeur prend alors le type indiqu. Cela entrane un changement de codage de la valeur.
exemple :
int i, j;
float isurj;
isurj= (float)i/j; // priorit de () sur /

Ici il est ncessaire de changer le type de i ou j en rel sinon la division donnera le quotient entier et non
rel.
Il y a donc transcodage de la valeur de i. Ce transcodage n'a lieu que le temps d'un calcul, la variable i
conservant toujours son type int.
Structure de cas
La syntaxe est la suivante :
switch(expression) {
case v1:
actions1;
break;
case v2:
actions2;
break;
Les bases 19
. .. .. .. .. ..
default: actions_sinon;
}

notes
La valeur de l'expression de contrle, ne peut tre qu'un entier ou un caractre.
l'expression de contrle est entoure de parenthses.
la clause default peut tre absente.
les valeurs vi sont des valeurs possibles de l'expression. Si l'expression a pour valeur vi, les actions
derrire la clause case vi sont excutes.
l'instruction break fait sortir de la structure de cas. Si elle est absente la fin du bloc d'instructions de
la valeur vi, l'excution se poursuit alors avec les instructions de la valeur vi+1.

Structure de rptition

Nombre de rptitions connu


Syntaxe
for (i=id;i<=if;i=i+ip){
actions;
}

Notes
les 3 arguments du for sont l'intrieur d'une parenthse.
les 3 arguments du for sont spars par des points-virgules.
chaque action du for est termine par un point-virgule.
l'accolade n'est ncessaire que s'il y a plus d'une action.
l'accolade n'est pas suivie de point-virgule.

Nombre de rptitions inconnu


Il existe de nombreuses structures en Java pour ce cas.
Structure tantque (while)
while(condition){
actions;
}

On boucle tant que la condition est vrifie. La boucle peut ne jamais tre excute.
notes :
la condition est entoure de parenthses.
chaque action est termine par point-virgule.
l'accolade n'est ncessaire que s'il y a plus d'une action.
l'accolade n'est pas suivie de point-virgule.
Structure rpter jusqu' (do while)
La syntaxe est la suivante :
do{
instructions;
}while(condition);

On boucle jusqu' ce que la condition devienne fausse ou tant que la condition est vraie. Ici la boucle est
faite au moins une fois.
notes:
la condition est entoure de parenthses.
chaque action est termine par point-virgule.
l'accolade n'est ncessaire que s'il y a plus d'une action.
l'accolade n'est pas suivie de point-virgule.
Structure pour gnrale (for)
La syntaxe est la suivante :
for(instructions_dpart;condition;instructions_fin_boucle){
instructions;
}

On boucle tant que la condition est vraie (value avant chaque tour de boucle). Instructions_dpart sont
effectues avant d'entrer dans la boucle pour la premire fois. Instructions_fin_boucle sont excutes aprs
chaque tour de boucle.
7

notes:
les 3 arguments du for sont l'intrieur de parenthses.
les 3 arguments du for sont spars par des points-virgules.
chaque action du for est termine par un point-virgule.
l'accolade n'est ncessaire que s'il y a plus d'une action.
l'accolade n'est pas suivie de point-virgule.
les diffrentes instructions dans instructions_depart et instructions_fin_boucle sont spares par des
virgules.
Exemples
Les programmes suivants calculent tous la somme des n premiers nombres entiers.
1 for(i=1, somme=0;i<=n;i=i+1)
somme=somme+a[i];
2 for (i=1, somme=0;i<=n;somme=somme+a[i], i=i+1);
3 i=1;somme=0;
while(i<=n)
{ somme+=i; i++; }
4 i=1; somme=0;
do somme+=i++;
while (i<=n);

Instructions de gestion de boucle


break
fait sortir de la boucle for, while, do ... while.
continue
fait passer l'itration suivante des boucles for, while, do ... while

La structure d'un programme Java


La fonction main, appele aussi mthode est excute la premire lors de l'excution d'un programme
Java. Elle doit avoir obligatoirement la signature prcdente :
public static void main(String arg[]){

ou
public static void main(String[] arg){

Le nom de l'argument arg peut tre quelconque. C'est un tableau de chanes de caractres reprsentant les
arguments de la ligne de commande. Nous y reviendrons un peu plus loin.
Arguments du programme principal
La fonction principale main admet comme paramtres un tableau de chanes : String[]. Ce tableau contient
les arguments de la ligne de commande utilise pour lancer l'application. Ainsi si on lance le programme P
avec la commande :
java P arg0 arg1 argn

et si la fonction main est dclare comme suit :


public static void main(String[] arg);

on aura arg[0]="arg0", arg[1]="arg1" Voici un exemple :

import java.io.*;
public class param1{
public static void main(String[] arg){
int i;
System.out.println("Nombre d'arguments="+arg.length);
for (i=0;i<arg.length;i++)
System.out.println("arg["+i+"]="+arg[i]);
}
}

Les rsultats obtenus sont les suivants :


dos>java param1 a b c
Nombre d'arguments=3
arg[0]=a
arg[1]=b
arg[2]=c

Classes et interfaces
Nous abordons maintenant, par l'exemple, la programmation objet. Un objet est une entit qui contient des
donnes qui dfinissent son tat (on les appelle des attributs ou proprits) et des fonctions (on les
appelle des mthodes). Un objet est cr selon un modle qu'on appelle une classe :
public class
type1 p1; //
type2 p2; //

type3 m3(){

}
type4 m4(){

C1{
proprit p1
proprit p2
// mthode m3
// mthode m4

Si O1 est un objet de type C1, O1.p1 dsigne la proprit p1 de O1 et O1.m1 la mthode m1 de O1.
Dfinition de la classe personne
La dfinition de la classe personne sera la suivante :
import java.io.*;
public class personne{
// attributs
private String prenom;
private String nom;
private int age;
// mthode
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
// mthode
public void identifie(){
System.out.println(prenom+","+nom+","+age);
}
}

Nous avons ici la dfinition d'une classe, donc un type de donne. Lorsqu'on va crer des variables de ce
type, on les appellera des objets. Une classe est donc un moule partir duquel sont construits des objets.

Les membres ou champs d'une classe peuvent tre des donnes ou des mthodes (fonctions). Ces
champs peuvent avoir l'un des trois attributs suivants :
priv
Un champ priv (private) n'est accessible que par les seules mthodes internes de la
classe
public
Un champ public est accessible par toute fonction dfinie ou non au sein de la classe
protg
Un champ protg (protected) n'est accessible que par les seules mthodes internes de la
classe ou d'un objet driv (voir ultrieurement le concept d'hritage).
La mthode initialise
Quel est le rle de la mthode initialise ? Parce que nom, prenom et age sont des donnes prives de la
classe personne, les instructions
personne p1;
p1.prenom="Jean";
p1.nom="Dupont";
p1.age=30;

sont illgales. Il nous faut initialiser un objet de type personne via une mthode publique. C'est le rle de
la mthode initialise. On crira :
personne p1;
p1.initialise("Jean","Dupont",30);

L'criture p1.initialise est lgale car initialise est d'accs public.


L'oprateur new
La squence d'instructions
personne p1;
p1.initialise("Jean","Dupont",30);

est incorrecte. L'instruction


personne p1;

dclare p1 comme une rfrence un objet de type personne. Cet objet n'existe pas encore et donc p1 n'est
pas initialis. C'est comme si on crivait :
personne p1=null;

Pour que p1 rfrence un objet, il faut crire :


personne p1=new personne();

Maintenant que p1 rfrence un objet, l'instruction d'initialisation de cet objet


p1.initialise("Jean","Dupont",30);

est valide.
Le mot cl this
Regardons le code de la mthode initialise
L'instruction this.prenom=P signifie que l'attribut prenom de l'objet courant (this) reoit la valeur P. Le
mot cl this dsigne l'objet courant : celui dans lequel se trouve la mthode excute.
La mthode initialise aurait aussi pu tre crite comme suit :
public void initialise(String P, String N, int age){
prenom=P;
nom=N;
this.age=age;
}

Lorsqu'une mthode d'un objet rfrence un attribut A de cet objet, l'criture this.A est implicite. On doit
l'utiliser explicitement lorsqu'il y a conflit d'identificateurs. C'est le cas de l'instruction :
this.age=age;

o age dsigne un attribut de l'objet courant ainsi que le paramtre age reu par la mthode. Il faut alors
lever l'ambigut en dsignant l'attribut age par this.age.
10

Un programme de test
Voici un programme de test :
public class test1{
public static void main(String arg[]){
personne p1=new personne();
p1.initialise("Jean","Dupont",30);
Classes et interfaces 33
p1.identifie();
}
}

La classe personne est dfinie dans le fichier source personne.java et est compile :
E:\data\serge\JAVA\BASES\OBJETS\2>javac personne.java
E:\data\serge\JAVA\BASES\OBJETS\2>dir
10/06/2002 09:21 473 personne.java
10/06/2002 09:22 835 personne.class
10/06/2002 09:23 165 test1.java

Nous faisons de mme pour le programme de test :


E:\data\serge\JAVA\BASES\OBJETS\2>javac test1.java
E:\data\serge\JAVA\BASES\OBJETS\2>dir
10/06/2002 09:21 473 personne.java
10/06/2002 09:22 835 personne.class
10/06/2002 09:23 165 test1.java
10/06/2002 09:25 418 test1.class

On peut s'tonner que le programme test1.java n'importe pas la classe personne avec une instruction :
import personne; Lorsque le compilateur rencontre dans le code source une rfrence de classe non dfinie
dans ce mme fichier source, il recherche la classe divers endroits :
dans les paquetages imports par les instructions import
dans le rpertoire partir duquel le compilateur a t lanc
Nous pouvons maintenant excuter le fichier test1.class :
E:\data\serge\JAVA\BASES\OBJETS\2>java test1
Jean,Dupont,30

Il est possible de rassembler plusieurs classes dans un mme fichier source.


Une autre mthode initialise
Considrons toujours la classe personne et rajoutons-lui la mthode suivante :
public void initialise(personne P){
prenom=P.prenom;
nom=P.nom;
this.age=P.age;
}

On a maintenant deux mthodes portant le nom initialise : c'est lgal tant qu'elles admettent des
paramtres diffrents. C'est le cas ici. Le paramtre est maintenant une rfrence P une personne. Les
attributs de la personne P sont alors affects l'objet courant (this).
Voici un test de la nouvelle classe personne :
11

// import personne;
import java.io.*;
public class test1{
public static void main(String arg[]){
personne p1=new personne();
p1.initialise("Jean","Dupont",30);
System.out.print("p1=");
p1.identifie();
personne p2=new personne();
p2.initialise(p1);
System.out.print("p2=");
p2.identifie();
}
}

et ses rsultats :
p1=Jean,Dupont,30
p2=Jean,Dupont,30

Constructeurs de la classe personne


Un constructeur est une mthode qui porte le nom de la classe et qui est appele lors de la cration de
l'objet. On s'en sert gnralement pour l'initialiser. C'est une mthode qui peut accepter des arguments
mais qui ne rend aucun rsultat. Son prototype ou sa dfinition nest prcd d'aucun type (mme pas
void).
Si une classe a un constructeur acceptant n arguments argi, la dclaration et l'initialisation d'un objet de
cette classe pourra se faire sous la forme :
classe objet =new classe(arg1,arg2, ... argn);
ou
classe objet;

objet=new classe(arg1,arg2, ... argn);


Crons deux constructeurs notre classe personne :
public class personne{
// attributs
private String prenom;
private String nom;
private int age;
// constructeurs
public personne(String P, String N, int age){
initialise(P,N,age);
}
public personne(personne P){
initialise(P);
}
// mthode
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
public void initialise(personne P){
this.prenom=P.prenom;
this.nom=P.nom;
this.age=P.age;
}
// mthode

12

public void identifie(){


System.out.println(prenom+","+nom+","+age);
}
}

Nos deux constructeurs se contentent de faire appel aux mthodes initialise correspondantes. On rappelle
que lorsque dans un constructeur, on trouve la notation initialise(P) par exemple, le compilateur traduit
par this.initialise(P). Dans le constructeur, la mthode initialise est donc appele pour travailler sur l'objet
rfrenc par this, c'est dire l'objet courant, celui qui est en cours de construction.
Voici un programme de test :
// import personne;
import java.io.*;
public class test1{
public static void main(String arg[]){
personne p1=new personne("Jean","Dupont",30);
System.out.print("p1=");
p1.identifie();
personne p2=new personne(p1);
System.out.print("p2=");
p2.identifie();
}
}

et les rsultats obtenus :


p1=Jean,Dupont,30
p2=Jean,Dupont,30

Les rfrences d'objets


Nous utilisons toujours la mme classe personne. Le programme de test devient le suivant :
// import personne;
import java.io.*;
public class test1{
public static void main(String arg[]){
// p1
personne p1=new personne("Jean","Dupont",30);
System.out.print("p1="); p1.identifie();
// p2 rfrence le mme objet que p1
personne p2=p1;
System.out.print("p2="); p2.identifie();
// p3 rfrence un objet qui sera une copie de l'objet rfrenc par p1
personne p3=new personne(p1);
System.out.print("p3="); p3.identifie();
// on change l'tat de l'objet rfrenc par p1
p1.initialise("Micheline","Benot",67);
System.out.print("p1="); p1.identifie();
// comme p2=p1, l'objet rfrenc par p2 a du changer d'tat
System.out.print("p2="); p2.identifie();
// comme p3 ne rfrence pas le mme objet que p1, l'objet rfrenc par p3 n'a pas du changer
System.out.print("p3="); p3.identifie();
}
}

Les rsultats obtenus sont les suivants :


p1=Jean,Dupont,30
p2=Jean,Dupont,30
p3=Jean,Dupont,30
p1=Micheline,Benot,67
p2=Micheline,Benot,67
p3=Jean,Dupont,30

Lorsqu'on dclare la variable p1 par


personne p1=new personne("Jean","Dupont",30);

p1 rfrence l'objet personne("Jean","Dupont",30) mais n'est pas l'objet lui-mme. En C, on dirait que
c'est un pointeur, c.a.d. l'adresse de l'objet cr. Si on crit ensuite :
p1=null

13

Ce n'est pas l'objet personne("Jean","Dupont",30) qui est modifi, c'est la rfrence p1 qui change de
valeur. L'objet personne("Jean","Dupont",30) sera "perdu" s'il n'est rfrenc par aucune autre variable.
Lorsqu'on crit :
personne p2=p1;

on initialise le pointeur p2 : il "pointe" sur le mme objet (il dsigne le mme objet) que le pointeur p1.
Ainsi si on modifie l'objet "point" (ou rfrenc) par p1, on modifie celui rfrenc par p2.
Lorsqu'on crit :
personne p3=new personne(p1);

il y a cration d'un nouvel objet, copie de l'objet rfrenc par p1. Ce nouvel objet sera rfrenc par p3.
Si on modifie l'objet "point" (ou rfrenc) par p1, on ne modifie en rien celui rfrenc par p3. C'est ce
que montrent les rsultats obtenus.
Un tableau de personnes
Un objet est une donne comme une autre et ce titre plusieurs objets peuvent tre rassembls dans un
tableau :
// import personne;
public class test1{
public static void main(String arg[]){
personne[] amis=new personne[3];
System.out.println("----------------");
amis[0]=new personne("Jean","Dupont",30);
amis[1]=new personne("Sylvie","Vartan",52);
amis[2]=new personne("Neil","Armstrong",66);
int i;
for(i=0;i<amis.length;i++)
amis[i].identifie();
}
}

L'instruction personne[] amis=new personne[3]; cre un tableau de 3 lments de type personne. Ces 3
lments sont initialiss ici avec la valeur null, c.a.d. qu'ils ne rfrencent aucun objet. De nouveau, par
abus de langage, on parle de tableau d'objets alors que ce n'est qu'un tableau de rfrences d'objets. La
cration du tableau d'objets, tableau qui est un objet lui-mme (prsence de new) ne cre donc en soi
aucun objet du type de ses lments : il faut le faire ensuite.
On obtient les rsultats suivants :
---------------Classes et interfaces 41
Constructeur personne(String, String, int)
Constructeur personne(String, String, int)
Constructeur personne(String, String, int)
Jean,Dupont,30
Sylvie,Vartan,52
Neil,Armstrong,66

14

Le mot-cl this est utile lorsquon a besoin de se rfrer l'instance de l'objet dans laquelle on se trouve.

L'hritage par l'exemple


Le but de l'hritage est de "personnaliser" une classe existante pour qu'elle satisfasse nos besoins.
Supposons qu'on veuille crer une classe enseignant : un enseignant est une personne particulire. Il a des
attributs qu'une autre personne n'aura pas : la matire qu'il enseigne par exemple. Mais il a aussi les
attributs de toute personne : prnom, nom et ge. Un enseignant fait donc pleinement partie de la classe
personne mais a des attributs supplmentaires. Plutt que d'crire une classe enseignant en partant de rien,
on prfrerait reprendre l'acquis de la classe personne qu'on adapterait au caractre particulier des
enseignants. C'est le concept d'hritage qui nous permet cela.
Pour exprimer que la classe enseignant hrite des proprits de la classe personne, on crira :
public class enseignant extends personne
personne est appele la classe parent (ou mre) et enseignant la classe drive (ou fille). Un objet
enseignant a toutes les qualits d'un objet personne : il a les mmes attributs et les mmes mthodes. Ces
attributs et mthodes de la classe parent ne sont pas rptes dans la dfinition de la classe fille : on se
contente d'indiquer les attributs et mthodes rajouts par la classe fille :
class enseignant extends personne{
// attributs
private int section;
// constructeur
public enseignant(String P, String N, int age,int section){
super(P,N,age);
this.section=section;
}
}

Nous supposons que la classe personne est dfinie comme suit :


public class personne{
private String prenom;
private String nom;
private int age;
public personne(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
public personne(personne P){
this.prenom=P.prenom;
this.nom=P.nom;
this.age=P.age;
}
public String identite(){
return "personne("+prenom+","+nom+","+age+")";
}
// accesseurs
public String getPrenom(){
return prenom;
}
public String getNom(){
return nom;
}
public int getAge(){
return age;
}
//modifieurs
public void setPrenom(String P){
this.prenom=P;
}

15

public void setNom(String N){


this.nom=N;
}
public void setAge(int age){
this.age=age;
}
}

La mthode identifie a t lgrement modifie pour rendre une chane de caractres identifiant la
personne et porte maintenant le nom identite. Ici la classe enseignant rajoute aux mthodes et attributs de
la classe personne :
un attribut section qui est le n de section auquel appartient l'enseignant dans le corps des enseignants
(une section par discipline en gros)
un nouveau constructeur permettant d'initialiser tous les attributs d'un enseignant
Construction d'un objet enseignant
Le constructeur de la classe enseignant est le suivant :
// constructeur
public enseignant(String P, String N, int age,int section){
super(P,N,age);
this.section=section;
}

L'instruction super(P,N,age) est un appel au constructeur de la classe parent, ici la classe personne. On
sait que ce constructeur initialise les champs prenom, nom et age de l'objet personne contenu l'intrieur
de l'objet tudiant. Cela parat bien compliqu et on pourrait prfrer crire :
// constructeur
public enseignant(String P, String N, int age,int section){
this.prenom=P;
this.nom=N
this.age=age
this.section=section;
}

C'est impossible. La classe personne a dclar privs (private) ses trois champs prenom, nom et age. Seuls
des objets de la mme classe ont un accs direct ces champs. Tous les autres objets, y compris des objets
fils comme ici, doivent passer par des mthodes publiques pour y avoir accs. Cela aurait t diffrent si la
classe personne avait dclar protgs (protected) les trois champs : elle autorisait alors des classes
drives avoir un accs direct aux trois champs. Dans notre exemple, utiliser le constructeur de la classe
parent tait donc la bonne solution et c'est la mthode habituelle : lors de la construction d'un objet fils, on
appelle d'abord le constructeur de l'objet parent puis on complte les initialisations propres cette fois
l'objet fils (section dans notre exemple).
Tentons un premier programme :
// import personne;
// import enseignant;
public class test1{
public static void main(String arg[]){
System.out.println(new enseignant("Jean","Dupont",30,27).identite());
}
}

Ce programme ce contente de crer un objet enseignant (new) et de l'identifier. La classe enseignant n'a
pas de mthode identit mais sa classe parent en a une qui de plus est publique : elle devient par hritage
une mthode publique de la classe enseignant.

16

Les fichiers source des classes sont rassembls dans un mme rpertoire puis compils :
E:\data\serge\JAVA\BASES\OBJETS\4>dir
10/06/2002 10:00
765 personne.java
10/06/2002 10:00
212 enseignant.java
10/06/2002 10:01
192 test1.java
E:\data\serge\JAVA\BASES\OBJETS\4>javac *.java
E:\data\serge\JAVA\BASES\OBJETS\4>dir
10/06/2002 10:00
765 personne.java
10/06/2002 10:00
212 enseignant.java
10/06/2002 10:01
192 test1.java
10/06/2002 10:02
316 enseignant.class
10/06/2002 10:02
1 146 personne.class
10/06/2002 10:02
550 test1.class

Le fichier test1.class est excut :


E:\data\serge\JAVA\BASES\OBJETS\4>java test1
personne(Jean,Dupont,30)

Surcharge d'une mthode


Dans l'exemple prcdent, nous avons eu l'identit de la partie personne de l'enseignant mais il manque
certaines informations propres la classe enseignant (la section). On est donc amen crire une mthode
permettant d'identifier l'enseignant :
class enseignant extends personne{
int section;
public enseignant(String P, String N, int age,int section){
super(P,N,age);
this.section=section;
}
public String identite(){
return "enseignant("+super.identite()+","+section+")";
}
}

La mthode identite de la classe enseignant s'appuie sur la mthode identite de sa classe mre
(super.identite) pour afficher sa partie "personne" puis complte avec le champ section qui est propre la
classe enseignant.
La classe enseignant dispose maintenant deux mthodes identite :
celle hrite de la classe parent personne
la sienne propre
Si E est un ojet enseignant, E.identite dsigne la mthode identite de la classe enseignant. On dit que la
mthode identite de la classe mre est "surcharge" par la mthode identite de la classe fille. De faon
gnrale, si O est un objet et M une mthode, pour excuter la mthode O.M, le systme cherche une
mthode M dans l'ordre suivant :
dans la classe de l'objet O
dans sa classe mre s'il en a une
dans la classe mre de sa classe mre si elle existe
etc
L'hritage permet donc de surcharger dans la classe fille des mthodes de mme nom dans la classe mre.
C'est ce qui permet d'adapter la classe fille ses propres besoins.
17

Considrons le mme exemple que prcdemment :


// import personne;
// import enseignant;
public class test1{
public static void main(String arg[]){
System.out.println(new enseignant("Jean","Dupont",30,27).identite());
}
}

Les rsultats obtenus sont cette fois les suivants :


enseignant(personne(Jean,Dupont,30),27)

Les interfaces
Une interface est une catgorie un peu spciale de classe abstract, dont le corps ne contient que la
dclaration de constantes et de mthodes.

Une classe ne peut hriter que d'une super classe, mais par contre peut implmenter plusieurs
interfaces.
Une classe doit implmenter toutes les mthodes de linterface implmente.
Une interface vide (comme l'interface Cloneable) permet de crer une catgorie de classes :
chaque classe implmentant ce type d'interface appartient telle ou telle catgorie. Pour tester si
la classe d'un objet appartient une catgorie, il suffit d'utiliser l'oprateur instanceof avec le nom
de l'interface.
Une interface peut servir aussi pour dclarer un ensemble de constantes, qui seront utilises dans
des classes sans lien d'hritage entre elles,

Classes d'usage courant


Les classes Java d'usage courant ont de nombreux attributs, mthodes et constructeurs. Le dtail de cellesci est disponible dans l'aide de Java. Si vous avez install le JDK de Sun dans le dossier <jdk>, la
documentation est disponible dans le dossier <jdk>\docs.
La classe String
La classe String reprsente les chanes de caractres. Soit nom une variable chane de caractres :
String nom;
nom est un rfrence d'un objet encore non initialis. On peut l'initialiser de deux faons :
nom="cheval" ou nom=new String("cheval")
Les deux mthodes sont quivalentes. Si on crit plus tard nom="poisson", nom rfrence alors un
nouvel objet. L'ancien objet String("cheval") est perdu et la place mmoire qu'il occupait sera rcupre.
La classe String est riche d'attributs et mthodes. En voici quelques-uns :
public char charAt(int i)
public int compareTo(chaine2)
public
boolean
equals(Object
anObject)
public String toLowerCase()
public String toUpperCase()

18

donne le caractre i de la chane, le premier caractre ayant l'indice 0. Ainsi


String("cheval").charAt(3) est gal 'v'
chaine1.compareTo(chaine2) compare chaine1 chaine2 et rend 0 si
chaine1=chaine2, 1 su chaine1>chaine2, -1 si chaine1<chaine2
chaine1.equals(chaine2) rend vrai si chaine1=chaine2, faux sinon
chaine1.toLowerCase() rend chaine1 en minuscules
chaine1.toUpperCase() rend chaine1 en majuscules

public String trim()


public String substring(int beginIndex,
int endIndex)
public char[] toCharArray()
int length()
int indexOf(String chaine2)
int indexOf(String chaine2,
startIndex)
int lastIndexOf(String chaine2)

int

boolean startsWith(String chaine2)


boolean endsWith(String chaine2)
boolean matches(String regex)
String[] split(String regex)

String replace(char oldChar, char


newChar)

chaine1.trim() rend chaine1 dbarasse de ses espaces de dbut et de fin


String("chapeau").subString(2,4) rend la chane "ape"
permet de mettre les caractres de la chane dans un tableau de caractres
nombre de caractres de la chane
rend la premire position de chaine2 dans la chane courante ou -1 si chaine2 n'est pas
prsente
rend la premire position de chaine2 dans la chane courante ou -1 si chaine2 n'est pas
prsente. La recherche commence partir du caractre n startIndex.
rend la dernire position de chaine2 dans la chane courante ou -1 si chaine2 n'est pas
prsente
rend vrai si la chane courante commence par chaine2
rend vrai si la chane courante finit par chaine2
rend vrai si la chane courante correspond l'expression rgulire regex.
La chane courante est compose de champs spars par une chane de caractres
modlise par l'expression rgulire regex. La mthode split permet de rcuprer les
champs dans un tableau.
remplace dans la chane courante le caractre oldChar par le caractre newChar.

Voici un programme d'exemple :


// imports
import java.io.*;
public class string1{
// une classe de dmonstration
public static void main(String[] args){
String uneChaine="l'oiseau vole au-dessus des nuages";
affiche("uneChaine="+uneChaine);
affiche("uneChaine.Length="+uneChaine.length());
affiche("chaine[10]="+uneChaine.charAt(10));
affiche("uneChaine.IndexOf(\"vole\")="+uneChaine.indexOf("vole"));
affiche("uneChaine.IndexOf(\"x\")="+uneChaine.indexOf("x"));
affiche("uneChaine.LastIndexOf('a')="+uneChaine.lastIndexOf('a'));
affiche("uneChaine.LastIndexOf('x')="+uneChaine.lastIndexOf('x'));
affiche("uneChaine.substring(4,7)="+uneChaine.substring(4,7));
affiche("uneChaine.ToUpper()="+uneChaine.toUpperCase());
affiche("uneChaine.ToLower()="+uneChaine.toLowerCase());
affiche("uneChaine.Replace('a','A')="+uneChaine.replace('a','A'));
String[] champs=uneChaine.split("\\s+");
for (int i=0;i<champs.length;i++){
affiche("champs["+i+"]=["+champs[i]+"]");
}//for
affiche("(\" abc \").trim()=["+" abc ".trim()+"]");
}//Main
// affiche
public static void affiche(String msg){
// affiche msg
System.out.println(msg);
}//affiche
}//classe

et les rsultats obtenus :


uneChaine=l'oiseau vole au-dessus des nuages
uneChaine.Length=34
chaine[10]=o
uneChaine.IndexOf("vole")=9
uneChaine.IndexOf("x")=-1
uneChaine.LastIndexOf('a')=30
uneChaine.LastIndexOf('x')=-1
uneChaine.substring(4,7)=sea
uneChaine.ToUpper()=L'OISEAU VOLE AU-DESSUS DES NUAGES
uneChaine.ToLower()=l'oiseau vole au-dessus des nuages
uneChaine.Replace('a','A')=l'oiseAu vole Au-dessus des nuAges

19

champs[0]=[l'oiseau]
champs[1]=[vole]
champs[2]=[au-dessus]
champs[3]=[des]
champs[4]=[nuages]
(" abc ").trim()=[abc]

La classe Vector
Un vecteur est un tableau dynamique dont les lments sont des rfrences d'objets. C'est donc un tableau
d'objets dont la taille peut varier au fil du temps ce qui n'est pas possible avec les tableaux statiques qu'on
a rencontrs jusqu'ici. Voici certains champs, constructeurs ou mthodes de cette classe :
public Vector()
public final int size()
public final void addElement(Object obj)
public final Object elementAt(int index)
public final Enumeration elements()
public final Object firstElement()
public final Object lastElement()
public final boolean isEmpty()
public final void removeElementAt(int index)
public final void removeAllElements()
public final String toString()

construit un vecteur vide


nombre d'lment du vecteur
ajoute l'objet rfrenc par obj au vecteur
rfrence de l'objet n index du vecteur - les indices
commencent 0
l'ensemble des lments du vecteur sous forme
d'numration
rfrence du premier lment du vecteur
rfrence du dernier lment du vecteur
rend vrai si le vecteur est vide
enlve l'lment d'indice index
vide le vecteur de tous ses lments
rend une chane d'identification du vecteur

Voici un programme de test :


// les classes importes
import java.util.*;
public class test1{
// le programme principal main - static - mthode de classe
public static void main(String arg[]){
// la cration des objets instances de classes
personne p=new personne("Jean","Dupont",30);
enseignant en=new enseignant("Paula","Hanson",56,27);
etudiant et=new etudiant("Chris","Garot",22,"19980405");
System.out.println("p="+p.toString());
System.out.println("en="+en.toString());
System.out.println("et="+et.toString());
// le polymorphisme
personne p2=(personne)en;
System.out.println("p2="+p2.toString());
personne p3=(personne)et;
System.out.println("p3="+p3.toString());
// un vecteur
Vector V=new Vector();
V.addElement(p);V.addElement(en);V.addElement(et);
System.out.println("Taille du vecteur V = "+V.size());
for(int i=0;i<V.size();i++){
p2=(personne) V.elementAt(i);
System.out.println("V["+i+"]="+p2.toString());
}
} // fin main
}// fin classe

Compilons ce programme :
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>dir

20

10/06/2002 10:41 1 134 personne.class


10/06/2002 10:41 619 enseignant.class
10/06/2002 10:41 610 etudiant.class
10/06/2002 10:42 1 035 test1.java
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>javac test1.java
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>dir
10/06/2002 10:41 1 134 personne.class
10/06/2002 10:41 619 enseignant.class
10/06/2002 10:41 610 etudiant.class
10/06/2002 10:42 1 035 test1.java
10/06/2002 10:43 1 506 test1.class

Excutons le fichier test1.class :


E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>java test1
p=personne(Jean,Dupont,30)
en=etudiant(personne(Paula,Hanson,56),27)
et=etudiant(personne(Chris,Garot,22),19980405)
p2=etudiant(personne(Paula,Hanson,56),27)
p3=etudiant(personne(Chris,Garot,22),19980405)
Taille du vecteur V = 3
V[0]=personne(Jean,Dupont,30)
V[1]=etudiant(personne(Paula,Hanson,56),27)
V[2]=etudiant(personne(Chris,Garot,22),19980405)

Par la suite, nous ne rpterons plus le processus de compilation et d'excution des programmes de tests. Il
suffit de reproduire ce qui a t fait ci-dessus.
La classe ArrayList
La classe ArrayList est analogue la classe Vector. Elle n'en diffre essentiellement que lorsquelle est
utilise simultanment par plusieurs threads d'excution. Les mthodes de synchronisation des threads
pour l'accs un Vector ou un ArrayList diffrent. En dehors de ce cas, on peut utiliser indiffremment
l'un ou l'autre. Voici certains champs, constructeurs ou mthodesde cette classe :
ArrayList()
int size()
void add(Object obj)
void add(int index,Object obj)
Object get(int index)
boolean isEmpty()
void remove(int index)
void clear()
Object[] toArray()
String toString()

construit un tableau vide


nombre d'lment du tableau
ajoute l'objet rfrenc par obj au tableau
ajoute l'objet rfrenc par obj au tableau en position index
rfrence de l'objet n index du tableau - les indices commencent 0
rend vrai si le tableau est vide
enlve l'lment d'indice index
vide le tableau de tous ses lments
met le tableau dynamique dans un tableau classique
rend une chane d'identification du tableau

Voici un programme de test :


// les classes importes
import java.util.*;
public class test1{
// le programme principal main - static - mthode de classe
public static void main(String arg[]){
// la cration des objets instances de classes
personne p=new personne("Jean","Dupont",30);
enseignant en=new enseignant("Paula","Hanson",56,27);
etudiant et=new etudiant("Chris","Garot",22,"19980405");
System.out.println("p="+p);
System.out.println("en="+en);
System.out.println("et="+et);

21

// le polymorphisme
personne p2=(personne)en;
System.out.println("p2="+p2);
personne p3=(personne)et;
System.out.println("p3="+p3);
// un vecteur
ArrayList personnes=new ArrayList();
personnes.add(p);personnes.add(en);personnes.add(et);
System.out.println("Nombre de personnes = "+personnes.size());
for(int i=0;i<personnes.size();i++){
p2=(personne) personnes.get(i);
System.out.println("personnes["+i+"]="+p2);
}
} // fin main
}// fin classe

Les rsultats obtenus sont les mmes que prcdemment.


Les fichiers texte
crire
Pour crire dans un fichier, il faut disposer d'un flux d'criture. On peut utiliser pour cela la classe
FileWriter. Les constructeurs souvent utiliss sont les suivants :
FileWriter(String fileName)
FileWriter(String fileName, boolean append)

cre le fichier de nom fileName - on peut ensuite crire


dedans - un ventuel fichier de mme nom est cras
idem - un ventuel fichier de mme nom peut tre utilis
en l'ouvrant en mode ajout (append=true)

La classe FileWriter offre un certain nombre de mthodes pour crire dans un fichier, mthodes hrites
de la classe Writer. Pour crire dans un fichier texte, il est prfrable d'utiliser la classe PrintWriter dont
les constructeurs souvent utiliss sont les suivants :
PrintWriter(Writer out)
PrintWriter(Writer out, Boolean autoflush)

l'argument est de type Writer, c.a.d. un flux d'criture


(dans un fichier, sur le rseau, )
idem. Le second argument gre la bufferisation des lignes.
Lorsqu'il est faux (son dfaut), les lignes crites sur le
fichier transitent par un buffer en mmoire. Lorsque celuici est plein, il est crit dans le fichier. Cela amliore les
accs disque. Ceci-dit quelquefois, ce comportement est
indsirable, notamment lorsqu'on crit sur le rseau.

Les mthodes utiles de la classe PrintWriter sont les suivantes :


void print(Type T)
void println(Type T)
void flush()
void close()

crit la donne T (String, int, .)


idem en terminant par une marque de fin de ligne
vide le buffer si on n'est pas en mode autoflush
ferme le flux d'criture

Voici un programme qui crit quelques lignes dans un fichier texte :


// imports
import java.io.*;
public class ecrire{
public static void main(String[] arg){
// ouverture du fichier
PrintWriter fic=null;

22

try{
fic=new PrintWriter(new FileWriter("out"));
} catch (Exception e){
Erreur(e,1);
}
// criture dans le fichier
try{
fic.println("Jean,Dupont,27");
fic.println("Pauline,Garcia,24");
fic.println("Gilles,Dumond,56");
} catch (Exception e){
Erreur(e,3);
}
// fermeture du fichier
try{
fic.close();
} catch (Exception e){
Erreur(e,2);
}
}// fin main
private static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}//Erreur
}//classe

Le fichier out obtenu l'excution est le suivant :


Jean,Dupont,27
Pauline,Garcia,24
Gilles,Dumond,56

Lire
Pour lire le contenu d'un fichier, il faut disposer d'un flux de lecture associ au fichier. On peut utiliser
pour cela la classe FileReader et le constructeur suivant :
FileReader(String nomFichier)

ouvre un flux de lecture partir du fichier indiqu. Lance


une exception si l'opration choue.

La classe FileReader possde un certain nombre de mthodes pour lire dans un fichier, mthodes hrites
de la classe Reader. Pour lire des lignes de texte dans un fichier texte, il est prfrable d'utiliser la classe
BufferedReader avec le constructeur suivant :
BufferedReader(Reader in)

ouvre un flux de lecture bufferis partir d'un flux


d'entre in. Ce flux de type Reader peut provenir du
clavier, d'un fichier, du rseau, ...

Les mthodes utiles de la classe BufferedReader sont les suivantes :


int read()
String readLine()
int read(char[] buffer, int offset, int taille)
void close()

lit un caractre
lit une ligne de texte
lit taille caractres dans le fichier et les met dans le
tableau buffer partir de la position offset.
ferme le flux de lecture

Voici un programme qui lit le contenu du fichier cr prcdemment :


// classes importes
import java.util.*;

23

import java.io.*;
public class lire{
public static void main(String[] arg){
personne p=null;
// ouverture du fichier
BufferedReader IN=null;
try{
IN=new BufferedReader(new FileReader("out"));
} catch (Exception e){
Erreur(e,1);
}
// donnes
String ligne=null;
String[] champs=null;
String prenom=null;
String nom=null;
int age=0;
// gestion des ventuelles erreurs
try{
while((ligne=IN.readLine())!=null){
champs=ligne.split(",");
prenom=champs[0];
nom=champs[1];
age=Integer.parseInt(champs[2]);
System.out.println(""+new personne(prenom,nom,age));
}// fin while
} catch (Exception e){
Erreur(e,2);
}
// fermeture fichier
try{
IN.close();
} catch (Exception e){
Erreur(e,3);
}
}// fin main
// Erreur
public static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}
}// fin classe

L'excution du programme donne les rsultats suivants :


personne(Jean,Dupont,27)
personne(Pauline,Garcia,24)
personne(Gilles,Dumond,56)

Sauvegarde d'un objet personne


Nous appliquons ce que nous venons de voir afin de fournir la classe personne une mthode permettant
de sauver dans un fichier
les attributs d'une personne. On rajoute la mthode sauveAttributs dans la dfinition de la classe personne :
// -----------------------------// sauvegarde dans fichier texte
// -----------------------------public void sauveAttributs(PrintWriter P){
P.println(""+this);
}

Prcdant la dfinition de la classe personne, on n'oubliera pas d'importer le paquetage java.io :


import java.io.*;

24

La mthode sauveAttributs reoit en unique paramtre le flux PrintWriter dans lequel elle doit crire. Un
programme de test pourrait tre le suivant :
// imports
import java.io.*;
// import personne;
public class sauver{
public static void main(String[] arg){
// ouverture du fichier
PrintWriter fic=null;
try{
fic=new PrintWriter(new FileWriter("out"));
} catch (Exception e){
Erreur(e,1);
}
// criture dans le fichier
try{
new personne("Jean","Dupont",27).sauveAttributs(fic);
new personne("Pauline","Garcia",24).sauveAttributs(fic);
new personne("Gilles","Dumond",56).sauveAttributs(fic);
} catch (Exception e){
Erreur(e,3);
}
// fermeture du fichier
try{
fic.close();
} catch (Exception e){
Erreur(e,2);
}
}// fin main
// Erreur
private static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}//Erreur
}//classe

Compilons et excutons ce programme :


E:\data\serge\JAVA\poly juin 2002\Chapitre
E:\data\serge\JAVA\poly juin 2002\Chapitre
10/06/2002 10:52 1 352 personne.class
10/06/2002 10:53 842 sauver.java
10/06/2002 10:53 1 258 sauver.class
E:\data\serge\JAVA\poly juin 2002\Chapitre
E:\data\serge\JAVA\poly juin 2002\Chapitre
10/06/2002 10:52 1 352 personne.class
10/06/2002 10:53 842 sauver.java
10/06/2002 10:53 1 258 sauver.class
10/06/2002 10:53 83 out
E:\data\serge\JAVA\poly juin 2002\Chapitre
personne(Jean,Dupont,27)
personne(Pauline,Garcia,24)
personne(Gilles,Dumond,56)

25

3\sauveAttributs>javac sauver.java
3\sauveAttributs>dir

3\sauveAttributs>java sauver
3\sauveAttributs>dir

3\sauveAttributs>more out

Les fichiers binaires


La classe RandomAccessFile
La classe RandomAccessFile permet de grer des fichiers binaires notamment ceux structure fixe comme on en connat
en langage C/C++. Voici quelques mthodes et constructeurs utiles :
RandomAccessFile(String nomFichier, String mode)

constructeur - ouvre le fichier indiqu dans le mode


indiqu. Celui-ci prend ses valeurs dans :
r : ouverture en lecture
rw : ouverture en lecture et criture

void writeTTT(TTT valeur)

crit valeur dans le fichier. TTT reprsente le type de


valeur. La reprsentation mmoire de valeur est crite
telle-quelle dans le fichier. On trouve ainsi writeBoolean,
writeByte,
writeInt,
writeDouble,
writeLong,
writeFloat,... Pour crire une chane, on utilise
writeBytes(String chaine).

TTT readTTT()

lit et rend une valeur de type TTT. On trouve ainsi


readBoolean, readByte, readInt, readDouble, readLong,
readFloat,... La mthode read() lit un octet.

long length()

taille du fichier en octets

long getFilePointer()

position courante du pointeur de fichier

void seek(long pos)

positionne le curseur de fichier l'octet pos

La classe article
Tous les exemples qui vont suivre utiliseront la classe article suivante :
// la structure article
private static class article{
// on dfinit la structure
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe article

La classe java article ci-dessus sera l'quivalent de la structure article suivante du C


struct article{
char code[4];
char nom[20];
double prix;
int stockActuel;
int stockMinimum;
}//structure

Ainsi nous limiterons le code 4 caractres et le nom 20.

26

crire un enregistrement

Le programme suivant crit un article dans un fichier appel "data" :


// classes importes
import java.io.*;
public class test1{
// teste l'criture d'une structure (au sens du C) dans un fichier binaire
// la structure article
private static class article{
// on dfinit la structure
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe article
public static void main(String arg[]){
// on dfinit le fichier binaire dans lequel seront rangs les articles
RandomAccessFile fic=null;
// on dfinit un article
article art=new article();
art.code="a100";
art.nom="velo";
art.prix=1000.80;
art.stockActuel=100;
art.stockMinimum=10;
// on dfinit le fichier
try{
fic=new RandomAccessFile("data","rw");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data",1);
}//try-catch
// on crit
try{
ecrire(fic,art);
} catch (IOException E){
erreur("Erreur lors de l'criture de l'enregistrement",2);
}//try-catch
// c'est fini
try{
fic.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data",2);
}//try-catch
}//main
// mthode d'criture
public static void ecrire(RandomAccessFile fic, article art) throws IOException{
// code
fic.writeBytes(art.code);
// le nom limit 20 caractres
art.nom=art.nom.trim();
int l=art.nom.length();
int nbBlancs=20-l;
if(nbBlancs>0){
String blancs="";
for(int i=0;i<nbBlancs;i++) blancs+=" ";
art.nom+=blancs;
} else art.nom=art.nom.substring(0,20);
fic.writeBytes(art.nom);
// le prix
fic.writeDouble(art.prix);
// les stocks
fic.writeInt(art.stockActuel);
fic.writeInt(art.stockMinimum);
}// fin crire
// ------------------------erreur
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fin erreur

27

}// fin class

C'est le programme suivant qui nous permet de vrifier que l'excution s'est correctement faite.

Lire un enregistrement
// classes importes
import java.io.*;
public class test2{
// teste l'criture d'une structure (au sens du C) dans un fichier binaire
// la structure article
private static class article{
// on dfinit la structure
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe article
public static void main(String arg[]){
// on dfinit le fichier binaire dans lequel seront rangs les articles
RandomAccessFile fic=null;
// on ouvre le fichier en lecture
try{
fic=new RandomAccessFile("data","r");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data",1);
}//try-catch
// on lit l'article unique du fichier
article art=new article();
try{
lire(fic,art);
} catch (IOException E){
erreur("Erreur lors de la lecture de l'enregistrement",2);
}//try-catch
// on affiche l'enregistrement lu
affiche(art);
// c'est fini
try{
fic.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data",2);
}//try-catch
}// fin main
// mthode de lecture
public static void lire(RandomAccessFile fic, article art) throws IOException{
// lecture code
art.code="";
for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
// nom
art.nom="";
for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
art.nom=art.nom.trim();
// prix
art.prix=fic.readDouble();
// stocks
art.stockActuel=fic.readInt();
art.stockMinimum=fic.readInt();
}// fin crire
// ---------------------affiche
public static void affiche(article art){
System.out.println("code : "+art.code);
System.out.println("nom : "+art.nom);
System.out.println("prix : "+art.prix);
System.out.println("Stock actuel : "+art.stockActuel);
System.out.println("Stock minimum : "+art.stockMinimum);
}// fin affiche
// ------------------------erreur
public static void erreur(String msg, int exitCode){
System.err.println(msg);

28

System.exit(exitCode);
}// fin erreur
}// fin class

Les rsultats d'excution sont les suivants :


E:\data\serge\JAVA\random>java test2
code : a100
nom : velo
prix : 1000.8
Stock actuel : 100
Stock minimum : 10

On rcupre bien l'enregistrement qui avait t crit par le programme d'criture.

29

Interfaces graphiques
Nous voyons tout d'abord quelles sont les classes de base qui nous permettent de construire une interface
graphique. Il y a deux groupes principaux de classes (librairies) utilises pour crer des fentres en Java :
AWT et Swing.
Au dbut, seule la librairie AWT (Abstract Window Toolkit) tait disponible pour travailler avec du
graphique. Cette librairie est un simple ensemble de classes telles que Button (bouton), TextField (champ
textuel), Label (libell) et autres. Peu aprs, une autre librairie, plus volue, apparut : Swing. Elle inclut
aussi les boutons, les champs textuels et d'autres contrles. Le nom des composants Swing commence par
la lettre J, par exemple JButton, JTextField, JLabel, etc.

Principaux lments de Swing


Voici les principaux objets qui constituent les applications Swing :

Une fentre ou un cadre (frame) qui peut tre cr en utilisant la classe JFrame.

Un panneau (panel) ou un carreau (pane) contenant tous les boutons, champs textuels, libells et
autres composants. Les panneaux sont crs l'aide de la classe JPanel.

Les contrles graphiques tels que les boutons (JButton), les champs textuels (JTextField), les
listes (JList)...

Les gestionnaires de disposition (layout managers) qui aident organiser tous ces boutons et
champs dans un panneau.

Habituellement, un programme cre une instance de JPanel et lui affecte un gestionnaire de disposition.
Ensuite, il peut crer des contrles graphiques et les ajouter au panneau. Enfin, il ajoute le panneau au
cadre, fixe les dimensions du cadre et le rend visible.

Une fentre simple


Considrons le code suivant :
// classes importes
import javax.swing.*;
import java.awt.*;
// la classe formulaire
public class form1 extends JFrame {
// le constructeur
public form1() {
// titre de la fentre
this.setTitle("Mon premier formulaire");
// dimensions de la fentre
this.setSize(new Dimension(300,100));
}//constructeur
// fonction de test
public static void main(String[] args) {
// on affiche le formulaire
new form1().setVisible(true);
}
}//classe

Une interface graphique drive en gnral de la classe de base JFrame :


public class form1 extends JFrame {

30

La classe de base JFrame dfinit une fentre de base avec des boutons de fermeture,
agrandissement/rduction, une taille ajustable, etc ... et gre les vnements sur ces objets graphiques. Ici
nous spcialisons la classe de base en lui fixant un titre et ses largeur (300 pixels) et hauteur (100 pixels).
Ceci est fait dans son constructeur :
// le constructeur
public form1() {
// titre de la fentre
this.setTitle("Mon premier formulaire");
// dimensions de la fentre
this.setSize(new Dimension(300,100));
}//constructeur

Le titre de la fentre est fixe par la mthode setTitle et ses dimensions par la mthode setSize. Cette
mthode accepte pour paramtre un objet Dimension(largeur,hauteur) o largeur et hauteur sont les
largeur et hauteur de la fentre exprimes en pixels.
La mthode main lance l'application graphique de la faon suivante :
new form1().setVisible(true);

Un formulaire de type form1 est alors cr (new form1()) et affich (setVisible(true)), puis l'application se
met l'coute des vnements qui se produisent sur le formulaire (clics, dplacements de souris, ...) et fait
excuter ceux que le formulaire gre. Ici, notre formulaire ne gre pas d'autres vnements que ceux grs
par la classe de base JFrame (clics sur boutons fermeture, agrandissement/rduction, changement de taille
de la fentre, dplacement de la fentre, ...).
Lorsqu'on teste ce programme en le lanant partir d'une fentre Dos par :
java form1

pour excuter le fichier form1.class, on constate que lorsqu'on ferme la fentre qui a t affiche, on ne
"rcupre pas la main" dans la fentre Dos, comme si le programme n'tait pas termin. C'est
effectivement le cas. L'excution du programme se fait de la faon suivante :
- au dpart, un premier thread d'excution est lanc pour excuter la mthode main
- lorsque celle-ci cre le formulaire et l'affiche, un second thread est cr pour grer spcifiquement les
vnements lis au formulaire
- aprs cette cration et dans notre exemple, le thread de la mthode main se termine et seul reste alors
le thread d'excution de l'interface graphique.
- lorsqu'on ferme la fentre, celle-ci disparat mais n'interrompt pas le thread dans lequel elle
s'excutait
- on est oblig pour l'instant d'arrter ce thread en faisant Ctrl-C dans la fentre Dos d'o a t lance
l'excution du programme.
Vrifions l'existence de deux threads spars, l'un dans lequel s'excute la mthode main, l'autre dans
lequel s'excute la fentre graphique :
// classes importes
import javax.swing.*;
import java.awt.*;
import java.io.*;
// la classe formulaire
public class form1 extends JFrame {
// le constructeur
public form1() {
// titre de la fentre
this.setTitle("Mon premier formulaire");
// dimensions de la fentre
this.setSize(new Dimension(300,100));
}//constructeur

31

// fonction de test
public static void main(String[] args) {
// suivi
System.out.println("Dbut du thread main");
// on affiche le formulaire
new form1().setVisible(true);
// suivi
System.out.println("Fin du thread main");
}//main
}//classe

L'excution donne les rsultats suivants :


On peut constater que le thread main est termin alors que la fentre est, elle, encore affiche. Le fait de
fermer la fentre ne termine pas le thread dans laquelle elle s'excutait. Pour arrter ce thread, on fait de
nouveau Ctrl-C dans la fentre Dos.
Pour terminer cet exemple, on notera les paquetages imports :
- javax.swing pour la classe JFrame
- java.awt pour la classe Dimension

la cration d'une calculatrice simple capable d'ajouter deux nombres et d'afficher le rsultat.
import javax.swing.*;
import java.awt.FlowLayout;
public class CalculatriceSimple {
public static void main(String[] args) {
// Cre un panneau
JPanel contenuFentre = new JPanel();
// Affecte un gestionnaire de disposition ce panneau
FlowLayout disposition = new FlowLayout();
contenuFentre.setLayout(disposition);
// Cre les contrles en mmoire
JLabel label1 = new JLabel("Nombre 1 :");
JTextField entre1 = new JTextField(10);
JLabel label2 = new JLabel("Nombre 2 :");
JTextField entre2 = new JTextField(10);
JLabel label3 = new JLabel("Somme :");
JTextField rsultat = new JTextField(10);
JButton lancer = new JButton("Ajouter");
// Ajoute les contrles au panneau
contenuFentre.add(label1);
contenuFentre.add(entre1);
contenuFentre.add(label2);
contenuFentre.add(entre2);
contenuFentre.add(label3);
contenuFentre.add(rsultat);
contenuFentre.add(lancer);
// Cre le cadre et y ajoute le panneau
JFrame cadre = new JFrame("Ma premire calculatrice");
cadre.setContentPane(contenuFentre);
// Positionne les dimensions et rend la fentre visible
cadre.setSize(400,100);
cadre.setVisible(true);
}
}

On va essayer d'amliorer lapparence de lapplication l'aide des gestionnaires de disposition.


Gestionnaires de disposition
Il y a dans Java des gestionnaires de disposition qui t'aident arranger les composants sur l'cran sans
avoir affecter des positions prcises aux contrles graphiques.
Swing propose les gestionnaires de disposition suivants :
FlowLayout (prsentation en file)
32

GridLayout (prsentation en grille)


BoxLayout (prsentation en lignes ou colonnes)
BorderLayout (prsentation avec bordures)
CardLayout (prsentation en pile)
GridBagLayout (prsentation en grille composite)
SpringLayout (prsentation avec ressorts)

Pour utiliser un gestionnaire de disposition, un programme doit l'instancier puis affecter cet objet un
conteneur (container), par exemple un panneau, comme dans la classe CalculatriceSimple.
FlowLayout
FlowLayout disposition = new FlowLayout();
this.setLayoutManager(disposition);

GridLayout
GridLayout disposition = new GridLayout(4,2);

Tu peux aussi fixer un espace, horizontal ou vertical, entre les cellules, par exemple cinq pixels (les
images sur l'cran de l'ordinateur sont constitues de minuscules points appels pixels).
GridLayout disposition = new GridLayout(4,2,5,5);

BorderLayout
La classe java.awt.BorderLayout partage la fentre en cinq zones : South (sud), West (ouest), North
(nord), East (est) et Center (centre). La rgion North est toujours en haut de la fentre, la rgion South en
bas ; la rgion West est gauche et la rgion East droite.
Par exemple, dans la calculatrice de la page suivante, le champ textuel qui affiche les nombres est situ
dans la zone North.
Voici comment crer une disposition BorderLayout et y placer un champ textuel :
BorderLayout disposition = new BorderLayout();
this.setLayoutManager(disposition);
JTextField affichageTexte = new JTextField(20);
this.add("North", affichageTexte);

BoxLayout
La classe javax.swing.BoxLayout permet de disposer de multiples composants soit horizontalement (selon
l'axe des X) ou verticalement (selon l'axe des Y). Contrairement au gestionnaire FlowLayout, les contrles
ne changent pas de ligne quand la fentre est retaille. Avec BoxLayout, les contrles peuvent avoir des
tailles diffrentes (ce qui n'est pas possible avec GridLayout).
Les deux lignes de code suivantes mettent en place une prsentation de type BoxLayout avec un
alignement vertical dans un JPanel.
JPanel panneauChiffres = new JPanel();
setLayout(new BoxLayout(panneauChiffres, BoxLayout.Y_AXIS));

Pour rendre ce code plus compact, je ne dclare pas de variable pour stocker une rfrence l'objet
BoxLayout ; je cre plutt une instance de cet objet et la passe immdiatement en argument la mthode
setLayout().

33

Combiner les gestionnaires de disposition


Le gestionnaire GridLayout ne permette pas de crer une fentre de calculatrice qui ressemble celle de
Microsoft Windows, parce que les cellules de cette calculatrice sont de tailles diffrentes le champ
textuel est plus large que les boutons, par exemple. Mais on peut combiner les gestionnaires de disposition
en utilisant des panneaux qui ont chacun leur propre gestionnaire de disposition.

Commenons avec une version un peu plus simple de la calculatrice :


import javax.swing.*;
import java.awt.GridLayout;
import java.awt.BorderLayout;
public class Calculatrice {
// Dclaration de tous les composants de la calculatrice.
JPanel contenuFentre;
JTextField champAffichage;
JButton bouton0;
JButton bouton1;
JButton bouton2;
JButton bouton3;
JButton bouton4;
JButton bouton5;
JButton bouton6;
JButton bouton7;
JButton bouton8;
JButton bouton9;
JButton boutonVirgule;
JButton boutonEgale;
JPanel panneauChiffres;
// Le constructeur cre les composants en mmoire
// et les ajoute au cadre en utilisant une combinaison
// de Borderlayout et Gridlayout
Calculatrice() {
contenuFentre = new JPanel();
// Affecte un gestionnaire de prsentation ce panneau
BorderLayout dispositionl = new BorderLayout();
contenuFentre.setLayout(dispositionl);
// Cre le champ d'affichage et le positionne dans
// la zone nord de la fentre
champAffichage = new JTextField(30);
contenuFentre.add("North", champAffichage);
// Cre les boutons en utilisant le constructeur de
// la classe JButton qui prend en paramtre le libell
// du bouton
bouton0 = new JButton("0");
bouton1 = new JButton("1");
bouton2 = new JButton("2");
bouton3 = new JButton("3");
bouton4 = new JButton("4");

34

bouton5 = new JButton("5");


bouton6 = new JButton("6");
bouton7 = new JButton("7");
bouton8 = new JButton("8");
bouton9 = new JButton("9");
boutonVirgule = new JButton(",");
boutonEgale = new JButton("=");
// Cre le panneau avec le quadrillage qui contient
// les 12 boutons les 10 boutons numriques et ceux
// reprsentant la virgule et le signe gale
panneauChiffres = new JPanel();
GridLayout disposition2 = new GridLayout(4, 3);
panneauChiffres.setLayout(disposition2);
// Ajoute les contrles au panneau panneauChiffres
panneauChiffres.add(bouton1);
panneauChiffres.add(bouton2);
panneauChiffres.add(bouton3);
panneauChiffres.add(bouton4);
panneauChiffres.add(bouton5);
panneauChiffres.add(bouton6);
panneauChiffres.add(bouton7);
panneauChiffres.add(bouton8);
panneauChiffres.add(bouton9);
panneauChiffres.add(bouton0);
panneauChiffres.add(boutonVirgule);
panneauChiffres.add(boutonEgale);
// Ajoute panneauChiffres la zone centrale de la
// fentre
contenuFentre.add("Center", panneauChiffres);
// Cre le cadre et lui affecte son contenu
JFrame frame = new JFrame("Calculatrice");
frame.setContentPane(contenuFentre);
// Affecte la fentre des dimensions suffisantes pour
// prendre en compte tous les contrles
frame.pack();
// Enfin, affiche la fentre
frame.setVisible(true);
}
public static void main(String[] args) {
Calculatrice calc = new Calculatrice();
}
}

Crer des fentres sans utiliser de gestionnaire de disposition


On peut fixer les coordonnes dans l'cran de chaque composant lorsque tu l'ajoutes la fentre. Dans ce
cas, ta classe doit annoncer explicitement qu'elle n'utilisera pas de gestionnaire de disposition.
contenuFentre.setLayout(null);

Le code doit affecter les coordonnes du coin suprieur gauche, la largeur et la hauteur de chaque
composant de la fentre. Cet exemple montre comment on peut donner un bouton une largeur de 40
pixels, une hauteur de 20 pixels et le placer 100 pixels droite et 200 pixels au-dessous du coin
suprieur gauche de la fentre :
JButton monBouton = new JButton("Nouvelle partie");
monBouton.setBounds(100, 200, 40, 20);

Voici la liste d-peu-prs tous les composants Swing disponible :


o JButton
o JTextArea
o JLabel
o JPasswordField
o JCheckBox
o JFormattedTextField
o JRadioButton
o JEditorPane
o JToggleButton
o JScrollBar
o JScrollPane
o JSlider
o JSpinner
o JProgressBar
o JTextFiel
o JComboBox
35

o
o
o
o
o
o
o

JList
JTabbedPane
JTable
JToolTip
JTree
JViewPort
ImageIcon

On peut aussi crer des menus (JMenu et JPopupMenu), des fentres la demande (popup), des cadres
imbriqus dans d'autres cadres (JInternalFrame) et utiliser les fentres standard de manipulation
d'informations : JFileChooser (choix de fichier), JColorChooser (choix de couleur) et JOptionPane (choix
d'option).
Java est accompagn d'une excellente application de dmonstration qui prsente tous les composants
Swing disponibles en action. Elle se trouve dans le rpertoire demo\jfc\SwingSet2 du rpertoire
d'installation de J2SDK. Ouvre simplement le fichier SwingSet2.html,
L'un des outils gratuits de conception d'interface utilisateur graphique (Graphic User Interface ou GUI),
permettant de crer facilement des composants Swing est jigloo.

Rcepteur d'vnements
Chaque composant d'une fentre peut traiter un certain nombre d'vnements ou, comme on dit, tre
l'coute (listen) de ces vnements,
Pour ragir aux actions de l'utilisateur il faut associer un gestionnaire d'vnements aux composants.
Le gestionnaire d'vnements qui gre le clic sur le bouton est l'interface ActionListener.
Celle-ci ne dfinit qu'une mthode :
void actionPerformed(ActionEvent e)

on va essayer un exemple qui affiche une bote de message (message box) depuis la mthode
actionPerformed().
on peux afficher n'importe quel message en utilisant la classe JOptionPane et sa mthode
showConfirmDialog().
Il y a diffrentes version de la mthode showConfirmDialog(). Nous allons utiliser celle qui prend quatre
arguments. Dans le code cidessous, le mot-cl null signifie que cette bote de message n'a pas de fentre
mre, le deuxime argument contient le message, le troisime le titre de la bote de message et le
quatrime te permet de choisir le(s) bouton(s) inclure dans la bote (PLAIN_MESSAGE signifie que
seul un bouton OK est affich).
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JOptionPane;
public class MoteurCalcul implements ActionListener {
public void actionPerformed(ActionEvent vnement) {
JOptionPane.showConfirmDialog(null,
"Quelque chose s'est produit...",
"Juste un test",
JOptionPane.PLAIN_MESSAGE);
}
}

Cest la mthode addActionListener qui permet d'associer au composant (Bouton) un gestionnaire


d'vnements.

La source d'un vnement


nous appelons la mthode getSource() de la classe ActionEvent pour savoir sur quel bouton a appuy
l'utilisateur.
Cette mthode retourne la source de l'vnement sous la forme d'une instance du type Object, qui est la
superclasse de toutes les classes Java, y compris les composants de fentre.
36

On sait bien que, dans notre fentre, seuls les boutons peuvent tre l'origine de l'vnement d'action.
alors on va faire une conversion de type explicite (casting) de l'Object retourn en un JButton,
JButton boutonCliqu = (JButton) vnement.getSource();

Si les vnements d'une fentre proviennent non seulement des boutons, mais aussi d'autres composants,
Nous ne voulons pas convertir n'importe quel objet en JButton, on doit utiliser l'oprateur Java spcial
instanceof pour effectuer la conversion de type approprie.
L'exemple suivant vrifie d'abord quel type d'objet est l'origine de l'vnement, puis effectue une
conversion de type, soit en JButton soit en JTextField :
public void actionPerformed(ActionEvent vnement) {
JTextField monChampAffichage = null;
JButton boutonCliqu = null;
Object sourceEvnement = vnement.getSource();
if (sourceEvnement instanceof JButton) {
boutonCliqu = (JButton) sourceEvnement;
}
else if (sourceEvnement instanceof JTextField) {
monChampAffichage = (JTextField) sourceEvnement;
}
}

Fin de la calculatrice
Intressons-nous quelques rgles (un algorithme) concernant la faon dont notre calculatrice doit
fonctionner :
1. L'utilisateur entre tous les chiffres du premier nombre.
2. Si l'utilisateur tape l'un des boutons d'action +, -, / ou *, alors stocker le premier nombre et l'action
slectionne dans des variables membres, puis effacer le nombre du champ textuel.
3. L'utilisateur entre un deuxime nombre et appuie sur le bouton gale.
4. Convertir la valeur de type String du champ textuel dans le type numrique double pour pouvoir
stocker de grands nombres dcimaux. Excuter l'action slectionne en utilisant cette valeur et le
nombre stock dans la variable l'tape 2.
5. Afficher le rsultat de l'tape 4 dans le champ textuel et stocker cette valeur dans la variable
utilise l'tape 2.
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.text.NumberFormat;
import java.text.ParsePosition;
import javax.swing.JButton;
public class MoteurCalcul implements ActionListener {
Calculatrice parent; // une rfrence la Calculatrice
char actionSlectionne = ' '; // +, -, /, ou *
double rsultatCourant = 0;
NumberFormat formatNombres = NumberFormat.getInstance();
// un objet capable de lire et prsenter les nombres
// Le constructeur stocke la rfrence la fentre
// Calculatrice dans la variable membre parent
MoteurCalcul(Calculatrice parent) {
this.parent = parent;
}
public void actionPerformed(ActionEvent vnement) {
// Retrouve la source de l'action
JButton boutonCliqu = (JButton) vnement.getSource();
String texteChampAffichage =
parent.champAffichage.getText();
double valeurAffiche = 0;
// Retrouve le nombre prsent dans le champ texte

37

// s'il n'est pas vide


if (!"".equals(texteChampAffichage)) {
valeurAffiche =
// analyse la chane de caractres
formatNombres.parse(
texteChampAffichage,
new ParsePosition(0) /* ne sert pas */).
// puis donne sa valeur en tant que double
doubleValue();
}
Object sourceEvnement = vnement.getSource();
// Pour chaque bouton d'action, mmorise l'action
// slectionne, +, -, /, ou *, stocke la valeur courante
// dans la variable rsultatCourant et vide le champ
// Affichage avant l'entre du nombre suivant
if (sourceEvnement == parent.boutonPlus) {
actionSlectionne = '+';
rsultatCourant = valeurAffiche;
parent.champAffichage.setText("");
}
else if (sourceEvnement == parent.boutonMoins) {
actionSlectionne = '-';
rsultatCourant = valeurAffiche;
parent.champAffichage.setText("");
}
else if (sourceEvnement == parent.boutonDiviser) {
actionSlectionne = '/';
rsultatCourant = valeurAffiche;
parent.champAffichage.setText("");
}
else if (sourceEvnement == parent.boutonMultiplier) {
actionSlectionne = '*';
rsultatCourant = valeurAffiche;
parent.champAffichage.setText("");
}
else if (sourceEvnement == parent.boutonEgale) {
// Effectue les calculs en fonction de actionSlectionne
// Modifie la valeur de la variable rsultatCourant
// et affiche le rsultat
if (actionSlectionne == '+') {
rsultatCourant += valeurAffiche;
// Convertit le rsultat en le transformant en String
// l'aide de formatNombres
parent.champAffichage.setText(
formatNombres.format(rsultatCourant));
}
else if (actionSlectionne == '-') {
rsultatCourant -= valeurAffiche;
parent.champAffichage.setText(
formatNombres.format(rsultatCourant));
}
else if (actionSlectionne == '/') {
rsultatCourant /= valeurAffiche;
parent.champAffichage.setText(
formatNombres.format(rsultatCourant));
}
else if (actionSlectionne == '*') {
rsultatCourant *= valeurAffiche;
parent.champAffichage.setText(
formatNombres.format(rsultatCourant));
}
}
else {
// Pour tous les boutons numriques, ajoute le libell
// du bouton au champ texte
String libellBoutonCliqu = boutonCliqu.getText();
parent.champAffichage.setText(texteChampAffichage +
libellBoutonCliqu);
}
}
}

La version finale de la fentre de la calculatrice ressemblera ceci :

38

Voici la version finale de la classe Calculatrice :


import javax.swing.*;
import java.awt.GridLayout;
import java.awt.BorderLayout;
public class Calculatrice {
// Dclare et instancie les composants de la fentre
JButton bouton0 = new JButton("0");
JButton bouton1 = new JButton("1");
JButton bouton2 = new JButton("2");
JButton bouton3 = new JButton("3");
JButton bouton4 = new JButton("4");
JButton bouton5 = new JButton("5");
JButton bouton6 = new JButton("6");
JButton bouton7 = new JButton("7");
JButton bouton8 = new JButton("8");
JButton bouton9 = new JButton("9");
JButton boutonVirgule = new JButton(",");
JButton boutonEgale = new JButton("=");
JButton boutonPlus = new JButton("+");
JButton boutonMoins = new JButton("-");
JButton boutonDiviser = new JButton("/");
JButton boutonMultiplier = new JButton("*");
JPanel contenuFentre = new JPanel();
JTextField champAffichage = new JTextField(30);
// Constructeur
Calculatrice() {
// Affecte le gestionnaire de disposition pour ce panneau
BorderLayout disposition = new BorderLayout();
contenuFentre.setLayout(disposition);
// Ajoute le champ d'affichage en haut de la fentre
contenuFentre.add("North", champAffichage);
// Cre le panneau avec le quadrillage qui contient
// 12 boutons les 10 boutons numriques et ceux
// reprsentant la virgule et le signe gale
JPanel panneauChiffres = new JPanel();
GridLayout dispositionChiffres = new GridLayout(4, 3);
panneauChiffres.setLayout(dispositionChiffres);
panneauChiffres.add(bouton1);
panneauChiffres.add(bouton2);
panneauChiffres.add(bouton3);
panneauChiffres.add(bouton4);
panneauChiffres.add(bouton5);
panneauChiffres.add(bouton6);
panneauChiffres.add(bouton7);
panneauChiffres.add(bouton8);
panneauChiffres.add(bouton9);
panneauChiffres.add(bouton0);
panneauChiffres.add(boutonVirgule);
panneauChiffres.add(boutonEgale);
// Ajoute le panneau des chiffres la zone centrale
// de la fentre
contenuFentre.add("Center", panneauChiffres);
// Cre le panneau avec le quadrillage qui contient 4
// boutons d'opration Plus, Moins, Diviser, Multiplier
JPanel panneauOprations = new JPanel();
GridLayout dispositionOprations = new GridLayout(4, 1);
panneauOprations.setLayout(dispositionOprations);
panneauOprations.add(boutonPlus);
panneauOprations.add(boutonMoins);
panneauOprations.add(boutonMultiplier);
panneauOprations.add(boutonDiviser);
// Ajoute le panneau des oprations la zone est
// de la fentre
contenuFentre.add("East", panneauOprations);
// Cre le cadre et lui affecte son contenu
JFrame frame = new JFrame("Calculatrice");
frame.setContentPane(contenuFentre);
// Affecte la fentre des dimensions suffisantes pour
// prendre en compte tous les contrles
frame.pack();
// Affiche la fentre
frame.setVisible(true);
// Instancie le rcepteur d'vnements et l'enregistre
// auprs de chaque bouton
MoteurCalcul moteurCalcul = new MoteurCalcul(this);
bouton0.addActionListener(moteurCalcul);
bouton1.addActionListener(moteurCalcul);
bouton2.addActionListener(moteurCalcul);
bouton3.addActionListener(moteurCalcul);
bouton4.addActionListener(moteurCalcul);
bouton5.addActionListener(moteurCalcul);

39

bouton6.addActionListener(moteurCalcul);
bouton7.addActionListener(moteurCalcul);
bouton8.addActionListener(moteurCalcul);
bouton9.addActionListener(moteurCalcul);
boutonVirgule.addActionListener(moteurCalcul);
boutonPlus.addActionListener(moteurCalcul);
boutonMoins.addActionListener(moteurCalcul);
boutonDiviser.addActionListener(moteurCalcul);
boutonMultiplier.addActionListener(moteurCalcul);
boutonEgale.addActionListener(moteurCalcul);
}
public static void main(String[] args) {
// Instancie la classe Calculatrice
Calculatrice calc = new Calculatrice();
}
}

Les gestionnaires d'vnements


Les principaux composants swing que nous allons prsenter sont les fentres (JFrame), les boutons
(JButton), les cases cocher (JCheckBox), les boutons radio (JButtonRadio), les listes droulantes
(JComboBox), les listes (JList), les variateurs (JScrollBar), les tiquettes (JLabel), les botes de saisie
monoligne (JTextField) ou multilignes (JTextArea), les menus (JMenuBar), les lments de menu
(JMenuItem).

Les tableaux suivants donnent une liste de quelques gestionnaires d'vnements et les vnements
auxquels ils sont lis.

Gestionnaire
ActionListener

Composant(s)
Mthode d'enregistrement
JButton , JCheckbox, public void addActionListener(ActionListener)
JButtonRadio,
JMenuItem, JTextField

ItemListener
InputMethodListener

JComboBox, JList
JTextField, JTextArea

CaretListener

JTextField, JTextArea

AdjustmentListener

JScrollBar

MouseMotionListener
WindowListener
MouseListener

FocusListener
KeyListener

40

JFrame

vnement

clic sur le bouton, la case


cocher, le bouton radio,
l'lment de menu, l'utilisateur a
tap [Entre] dans la zone de
saisie
public void addItemListener(ItemListener) public L'lment slectionn a chang
void
le texte de la zone de saisie a
addMethodInputListener(InputMethodListener)
chang ou le curseur de saisie a
chang de position
public void addcaretListener(CaretListener)
Le curseur de saisie a chang de
Position
public void
addAdjustmentListener(AdjustmentListener)
public void
addMouseMotionListener(MouseMotionListener)
public void addWindowlistener(WindowListener)
public void addMouselistener(MouseListener)

public void addFocuslistener(FocusListener)


public void addKeylistener(KeyListener)

la valeur du variateur a chang


la souris a boug
vnement fentre
vnements souris (clic,
entre/sortie du domaine d'un
composant, bouton press,
relche)
vnement focus (obtenu, perdu)
vnement clavier( touche tape,
presse, relache)

Composant
JButton
JCheckbox
JCheckboxMenuItem
JComboBox
Container
JComponent

JFrame
JList
JMenuItem
JPanel
JScrollPane
JScrollBar
JTextComponent

JTextArea
JTextField

Mthode d'enregistrement des gestionnaires


d'venements
public void addActionListener(ActionListener)
public void addItemListener(ItemListener)
public void addItemListener(ItemListener)
public void addItemListener(ItemListener)
public void addActionListener(ActionListener)
public void addContainerListener(ContainerListener)
public void addComponentListener(ComponentListener)
public void addFocusListener(FocusListener)
public void addKeyListener(KeyListener)
public void addMouseListener(MouseListener)
public void
addMouseMotionListener(MouseMotionListener)
public void addWindowlistener(WindowListener)
public void addItemListener(ItemListener)
public void addActionListener(ActionListener)
comme Container
comme Container
public void addAdjustmentListener(AdjustmentListener)
public void
addInputMethodListener(InputMethodListener)
public void addCaretListener(CaretListener)
comme JTextComponent
comme JTextComponent
public void addActionListener(ActionListener

Tous les composants, sauf ceux du type TextXXX, tant drivs de la classe JComponent, possdent
galement les mthodes associes cette classe.
Les mthodes des gestionnaires d'vnements
Le tableau suivant liste les mthodes que doivent implmenter les diffrents gestionnaires d'vnements.
Interface
ActionListener
AdjustmentListener
ComponentListener

ContainerListener
FocusListener
ItemListener
KeyListener

MouseListener

MouseMotionListener
TextListener
InputmethodListener
CaretLisetner
WindowListener

41

Mthodes
public void actionPerformed(ActionEvent)
public void adjustmentValueChanged(AdjustmentEvent)
public void componentHidden(ComponentEvent)
public void componentMoved(ComponentEvent)
public void componentResized(ComponentEvent)
public void componentShown(ComponentEvent)
public void componentAdded(ContainerEvent)
public void componentRemoved(ContainerEvent)
public void focusGained(FocusEvent)
public void focusLost(FocusEvent)
public void itemStateChanged(ItemEvent)
public void keyPressed(KeyEvent)
public void keyReleased(KeyEvent)
public void keyTyped(KeyEvent)
public void mouseClicked(MouseEvent)
public void mouseEntered(MouseEvent)
public void mouseExited(MouseEvent)
public void mousePressed(MouseEvent)
public void mouseReleased(MouseEvent)
public void mouseDragged(MouseEvent)
public void mouseMoved(MouseEvent)
public void textValueChanged(TextEvent)
public void InputMethodTextChanged(InputMethodEvent)
public void caretPositionChanged(InputMethodEvent)
public void caretUpdate(CaretEvent)
public void windowActivated(WindowEvent)
public void windowClosed(WindowEvent)
public void windowClosing(WindowEvent)
public void windowDeactivated(WindowEvent)
public void windowDeiconified(WindowEvent)
public void windowIconified(WindowEvent)
public void windowOpened(WindowEvent)

Les classes adaptateurs


Comme nous l'avons vu pour l'interface WindowListener, il existe des classes nommes XXXAdapter qui
implmentent les interfaces XXXListener avec des mthodes vides. Un gestionnaire d'vnements driv
d'une classe XXXAdapter peut alors n'implmenter qu'une partie des mthodes de l'interface XXXListener,
celles dont lapplication a besoin.
Supposons qu'on veuille grer les clics de souris sur un composant Frame f1. On pourrait lui associer un
gestionnaire d'vnements par :
f1.addMouseListener(new gestionnaireSouris());

et crire :
public class gestionnaireSouris implements MouseListener{
// on crit les 5 mthodes de l'interface MouseListener
// mouseClicked, ..., mouseReleased)
}// fin classe

Comme on ne souhaite grer que les clics de souris, il est cependant prfrable d'crire :
public class gestionnaireSouris extends MouseAdapter{
// on crit une seule mthode, celle qui gre les clics de souris
public void mouseClicked(MouseEvent evt){

}
}// fin classe

Le tableau suivant donne les classes adapteurs des diffrents gestionnaires d'vnements :
Gestionnaire d'vnements
ComponentListener
ContainerListener
FocusListener
KeyListener
MouseListener
MouseMotionListener
WindowListener

42

Adaptateur
ComponentAdapter
ContainerAdapter
FocusAdapter
KeyAdapter
MouseAdapter
MouseMotionAdapter
WindowAdapter

Les Threads d'excution


Tous nos programmes s'excutent en squence une action aprs l'autre. Si un programme appelle deux
mthodes, la seconde mthode attend que la premire se soit termine. Autrement dit, chacun de nos
programmes n'a qu'un fil d'excution (thread of execution).
Lorsqu'on lance une application, elle s'excute dans un flux d'excution appel un thread. La classe
modlisant un thread est la classe java.lang.Thread dont voici quelques proprits et mthodes :
currentThread()
setName()
getName()
isAlive()
start()
run()
sleep(n)
join()

donne le thread actuellement en cours d'excution


fixe le nom du thread
nom du thread
indique si le thread est actif(true) ou non (false)
lance l'excution d'un thread
mthode excute automatiquement aprs que la mthode start prcdente ait t excute
arrte l'excution d'un thread pendant n millisecondes
opration bloquante - attend la fin du thread pour passer l'instruction suivante

Les constructeurs les plus couramment utiliss sont les suivants :


Thread()

Thread(Runnable object)

cre une rfrence sur une tche asynchrone. Celle-ci est encore inactive.
La tche cre doit possder la mthode run : ce sera le plus souvent une
classe drive de Thread qui sera utilise.
idem mais c'est l'objet Runnable pass en paramtre qui implmente la
mthode run.

Regardons une premire application mettant en vidence l'existence d'un thread principal d'excution,
celui dans lequel s'excute la fonction main d'une classe :
// utilisation de threads
import java.io.*;
import java.util.*;
public class thread1{
public static void main(String[] arg)throws Exception {
// init thread courant
Thread main=Thread.currentThread();
// affichage
System.out.println("Thread courant : " + main.getName());
// on change le nom
main.setName("myMainThread");
// vrification
System.out.println("Thread courant : " + main.getName());
// boucle infinie
while(true){
// on rcupre l'heure
Calendar calendrier=Calendar.getInstance();
String H=calendrier.get(Calendar.HOUR_OF_DAY)+":"
+calendrier.get(Calendar.MINUTE)+":"
+calendrier.get(Calendar.SECOND);
// affichage
System.out.println(main.getName() + " : " +H);
// arrt temporaire
Thread.sleep(1000);
}//while
}//main
}//classe

43

L'exemple prcdent illustre les points suivants :


la fonction main s'excute bien dans un thread
on a accs aux caractristiques de ce thread par Thread.currentThread()
le rle de la mthode sleep. Ici le thread excutant main se met en sommeil rgulirement pendant
1 seconde entre deux affichages.

1. Un thread T peut tre cr de diverses faons


en drivant la classe Thread et en redfinissant la mthode run de celle-ci.
en implmentant l'interface Runnable dans une classe et en utilisant le constructeur new
Thread(Runnable). Runnable est une interface qui ne dfinit qu'une seule mthode : public void
run(). L'argument du constructeur prcdent est donc toute instance de classe implmentant cette
mthode run.
Dans l'exemple qui suit, les threads sont construits l'aide d'une classe anonyme drivant la classe Thread:
// on cre le thread i
tches[i]=new Thread() {
public void run() {
affiche();
}
};//df tches[i]

La mthode run se contente ici de renvoyer sur une mthode affiche.


2. L'excution du thread T est lanc par T.start() : cette mthode appartient la classe Thread et opre
un certain nombre d'initialisations puis lance automatiquement la mthode run du Thread ou de
l'interface Runnable. Le programme qui excute l'instruction T.start() n'attend pas la fin de la tche T:
il passe aussitt l'instruction qui suit. On a alors deux tches qui s'excutent en parallle. Elles
doivent souvent pouvoir communiquer entre elles pour savoir o en est le travail commun raliser.
C'est le problme de synchronisation des threads.
3. Une fois lanc, le thread s'excute de faon autonome. Il s'arrtera lorsque la fonction run qu'il
excute aura fini son travail.
4. On peut attendre la fin de l'excution du Thread T par T.join(). On a l une instruction bloquante : le
programme qui l'excute est bloqu jusqu' ce que la tche T ait termin son travail. C'est galement
un moyen de synchronisation.

Examinons le programme suivant :


// utilisation de threads
import java.io.*;
import java.util.*;
public class thread2{
public static void main(String[] arg) {
// init thread courant
Thread main=Thread.currentThread();
// on donne un nom au thread courant
main.setName("myMainThread");
// dbut de main
System.out.println("dbut du thread " +main.getName());
// cration de threads d'excution
Thread[] tches=new Thread[5];
for(int i=0;i<tches.length;i++){
// on cre le thread i
tches[i]=new Thread() {
public void run() {
affiche();

44

}
};//df tches[i]
// on fixe le nom du thread
tches[i].setName(""+i);
// on lance l'excution du thread i
tches[i].start();
}//for
// fin de main
System.out.println("fin du thread " +main.getName());
}//Main
public static void affiche() {
// on rcupre l'heure
Calendar calendrier=Calendar.getInstance();
String H=calendrier.get(Calendar.HOUR_OF_DAY)+":"
+calendrier.get(Calendar.MINUTE)+":"
+calendrier.get(Calendar.SECOND);
// affichage dbut d'excution
System.out.println("Dbut d'excution de la mthode affiche dans le Thread " +
Thread.currentThread().getName()+ " : " + H);
// mise en sommeil pendant 1 s
try{
Thread.sleep(1000);
}catch (Exception ex){}
// on rcupre l'heure
calendrier=Calendar.getInstance();
H=calendrier.get(Calendar.HOUR_OF_DAY)+":"
+calendrier.get(Calendar.MINUTE)+":"
+calendrier.get(Calendar.SECOND);
// affichage fin d'excution
System.out.println("Fin d'excution de la mthode affiche dans le Thread "
+Thread.currentThread().getName()+ " : " + H);
}// affiche
}//classe

Le thread principal, celui qui excute la fonction main, cre 5 autres threads chargs d'excuter la mthode
statique affiche. Les
rsultats sont les suivants :
dbut du thread myMainThread
Dbut d'excution de la mthode affiche dans le Thread
fin du thread myMainThread
Dbut d'excution de la mthode affiche dans le Thread
Dbut d'excution de la mthode affiche dans le Thread
Dbut d'excution de la mthode affiche dans le Thread
Dbut d'excution de la mthode affiche dans le Thread
Fin d'excution de la mthode affiche dans le Thread 0
Fin d'excution de la mthode affiche dans le Thread 1
Fin d'excution de la mthode affiche dans le Thread 2
Fin d'excution de la mthode affiche dans le Thread 3
Fin d'excution de la mthode affiche dans le Thread 4

0 : 15:48:3
1
2
3
4
:
:
:
:
:

: 15:48:3
: 15:48:3
: 15:48:3
: 15:48:3
15:48:4
15:48:4
15:48:4
15:48:4
15:48:4

Modifions notre programme pour le terminer la mthode main par les instructions :
// fin de main
System.out.println("fin du thread " +main.getName());
// arrt de l'application
System.exit(0);

L'excution du nouveau programme donne :


dbut du thread myMainThread
Dbut d'excution de la mthode
Dbut d'excution de la mthode
Dbut d'excution de la mthode
Dbut d'excution de la mthode
fin du thread myMainThread
Dbut d'excution de la mthode

45

affiche
affiche
affiche
affiche

dans
dans
dans
dans

le
le
le
le

Thread
Thread
Thread
Thread

0
1
2
3

:
:
:
:

16:5:45
16:5:45
16:5:45
16:5:45

affiche dans le Thread 4 : 16:5:45

Ds que la mthode main excute l'instruction :


System.exit(0);

elle arrte tous les threads de l'application et non simplement le thread main. La mthode main pourrait
vouloir attendre la fin d'excution des threads qu'elle a crs avant de se terminer elle-mme. Cela peut se
faire avec la mthode join de la classe Thread :
// attente de tous les threads
for(int i=0;i<tches.length;i++){
// on attend le thread i
tches[i].join();
}//for
// fin de main
System.out.println("fin du thread " +main.getName());
// arrt de l'application
System.exit(0);

On obtient alors les rsultats suivants :


dbut du thread myMainThread
Dbut d'excution de la mthode affiche dans le Thread
Dbut d'excution de la mthode affiche dans le Thread
Dbut d'excution de la mthode affiche dans le Thread
Dbut d'excution de la mthode affiche dans le Thread
Dbut d'excution de la mthode affiche dans le Thread
Fin d'excution de la mthode affiche dans le Thread 0
Fin d'excution de la mthode affiche dans le Thread 1
Fin d'excution de la mthode affiche dans le Thread 2
Fin d'excution de la mthode affiche dans le Thread 3
Fin d'excution de la mthode affiche dans le Thread 4
fin du thread myMainThread

0
1
2
3
4
:
:
:
:
:

: 16:11:9
: 16:11:9
: 16:11:9
: 16:11:9
: 16:11:9
16:11:10
16:11:10
16:11:10
16:11:10
16:11:10

Voil deux programmes trs simples pour mieux te faire comprendre pourquoi les fils d'excution sont
ncessaires.
import javax.swing.*;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ExempleSansFils extends JFrame
implements ActionListener {
// Constructeur
ExempleSansFils() {
// Cre un cadre contenant un bouton et un champ textuel
GridLayout disposition = new GridLayout(2,1);
this.getContentPane().setLayout(disposition);
JButton monBouton = new JButton("Tuer le temps");
monBouton.addActionListener(this);
this.getContentPane().add(monBouton);
this.getContentPane().add(new JTextField());
}
// Traite les clics sur le bouton
public void actionPerformed(ActionEvent vnement) {
// Tue juste un peu le temps pour montrer que
// les contrles de la fentre sont verrouills.
for (int i = 0; i < 30000; i++) {
this.setTitle("i = " + i);
}
}
public static void main(String[] args) {
// Cre une instance du cadre
ExempleSansFils maFentre = new ExempleSansFils();
// Permet la fermeture de la fentre par clic sur la
// petite croix dans le coin.
maFentre.setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE);

46

// Affecte sa taille au cadre coordonnes du coin haut


// gauche, largeur et hauteur.
maFentre.setBounds(0, 0, 200, 100);
// Rend la fentre visible.
maFentre.setVisible(true);
}
}

La version suivante de cette petite fentre cre et lance un thread spar pour la boucle ;
En Java, on peut crer un fil d'excution par l'un des moyens suivants :
1. Crer une instance de la classe Java Thread et lui passer un objet qui implante l'interface Runnable. Si
cette classe implante l'interface Runnable, le code ressemble ceci :
Thread travailleur = new Thread(this);

Cette interface t'impose d'crire dans la mthode run() le code qui doit tre excut comme un fil
d'excution spar. Mais pour lancer le fil d'excution, tu dois appeler la mthode start(), qui va en fait
appeler ta mthode run(). D'accord, c'est un peu troublant, mais c'est comme a que tu dmarres le fil
d'excution :
travailleur.start();

2. Crer une sous-classe de la classe Thread et y implanter la mthode run(). Pour dmarrer le fil
d'excution, appeler la mthode start().
public class MonFil extends Thread {
public static void main(String[] args) {
MonFil travailleur = new MonFil();
travailleur.start();
}
public void run() {
// Place ton code ici.
}
}

J'utilise la premire mthode dans la classe ExempleAvecFils parce que cette classe hrite dj de JFrame
et qu'on ne peut pas hriter de plus d'une classe en Java.
import javax.swing.*;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ExempleAvecFils extends JFrame
implements ActionListener, Runnable {
// Constructeur
ExempleAvecFils() {
// Cre un cadre contenant un bouton et un champ textuel.
GridLayout disposition = new GridLayout(2,1);
this.getContentPane().setLayout(disposition);
JButton monBouton = new JButton("Tuer le temps");
monBouton.addActionListener(this);
this.getContentPane().add(monBouton);
this.getContentPane().add(new JTextField());
}
public void actionPerformed(ActionEvent vnement) {
// Cre un fil et excute le code "tuer le temps"
// sans bloquer la fentre.
Thread travailleur = new Thread(this);
travailleur.start(); // Ceci appelle la mthode run()
}
public void run() {
// Tue juste un peu le temps pour montrer que
// les contrles de la fentre NE sont PAS verrouills.

47

for (int i = 0; i < 30000; i++) {


this.setTitle("i = " + i);
}
}
public static void main(String[] args) {
ExempleAvecFils maFentre = new ExempleAvecFils();
// Permet la fermeture de la fentre par clic sur la
// petite croix dans le coin.
maFentre.setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE);
// Affecte sa taille au cadre et le rend visible.
maFentre.setBounds(0, 0, 200, 100);
maFentre.setVisible(true);
}
}

48

Programmation TCP-IP
Gestion des adresses rseau en Java
Chaque machine de l'Internet est identifie par une adresse ou un nom uniques. Ces deux entits sont
gres sous Java par la classe InetAddress dont voici quelque mthodes :
byte [] getAddress()

donne les 4 octets de l'adresse IP de l'instance InetAddress


courante
String getHostAddress()
donne l'adresse IP de l'instance InetAddress courante
String getHostName()
donne le nom Internet de l'instance InetAddress courante
String toString()
donne l'identit adresse IP/ nom internet de l'instance InetAddress
courante
InetAddress getByName(String Host) cre l'instance InetAddress de la machine dsigne par Host.
Gnre une exception si Host est inconnu. Host peut tre le nom
internet d'une machine ou son adresse IP sous la forme I1.I2.I3.I4
InetAddress getLocalHost()
cre l'instance InetAddress de la machine sur laquelle s'excute le
programme contenant cette instruction.

Quelques exemples
Identifier la machine locale
import java.net.*;
public class localhost{
public static void main (String arg[]){
try{
InetAddress adresse=InetAddress.getLocalHost();
byte[] IP=adresse.getAddress();
System.out.print("IP=");
int i;
for(i=0;i<IP.length-1;i++) System.out.print(IP[i]+".");
System.out.println(IP[i]);
System.out.println("adresse="+adresse.getHostAddress());
System.out.println("nom="+adresse.getHostName());
System.out.println("identit="+adresse);
} catch (UnknownHostException e){
System.out.println ("Erreur getLocalHost : "+e);
}// fin try
}// fin main
}// fin class

Les rsultats de l'excution sont les suivants :


IP=127.0.0.1
adresse=127.0.0.1
nom=tahe
identit=tahe/127.0.0.1

Les rsultats de l'excution sont les suivants :


IP=127.0.0.1
adresse=127.0.0.1
nom=tahe
identit=tahe/127.0.0.1

Chaque machine a une adresse IP interne qui est 127.0.0.1. Lorsqu'un programme utilise cette adresse
rseau, il utilise la machine sur laquelle il fonctionne. L'intrt de cette adresse est qu'elle ne ncessite pas
de carte rseau. On peut donc tester des programmes rseau sans tre connect un rseau. Une autre
faon de dsigner la machine locale est d'utiliser le nom localhost.
49

Identifier une machine quelconque


import java.net.*;
public class getbyname{
public static void main (String arg[]){
String nomMachine;
// on rcupre l'argument
if(arg.length==0)
nomMachine="localhost";
else nomMachine=arg[0];
// on tente d'obtenir l'adresse de la machine
try{
InetAddress adresse=InetAddress.getByName(nomMachine);
System.out.println("IP : "+ adresse.getHostAddress());
System.out.println("nom : "+ adresse.getHostName());
System.out.println("identit : "+ adresse);
} catch (UnknownHostException e){
System.out.println ("Erreur getByName : "+e);
}// fin try
}// fin main
}// fin class

Avec l'appel java getbyname, on obtient les rsultats suivants :


IP : 127.0.0.1
nom : localhost
identit : localhost/127.0.0.1

Avec l'appel java getbyname shiva.istia.univ-angers.fr, on obtient :


IP : 193.52.43.5
nom : shiva.istia.univ-angers.fr
identit : shiva.istia.univ-angers.fr/193.52.43.5

Avec l'appel java getbyname www.ibm.com, on obtient :


IP : 204.146.18.33
nom : www.ibm.com
identit : www.ibm.com/204.146.18.33

La relation client-serveur
Souvent, la communication sur Internet est dissymtrique : la machine A initie une connexion pour
demander un service la machine B : il prcise qu'il veut ouvrir une connexion avec le service SB1 de la
machine B. Celle-ci accepte ou refuse. Si elle accepte, la machine A peut envoyer ses demandes au service
SB1. Celles-ci doivent se conformer au protocole de dialogue compris par le service SB1. Un dialogue
demande-rponse s'instaure ainsi entre la machine A qu'on appelle machine cliente et la machine B qu'on
appelle machine serveur. L'un des deux partenaires fermera la connexion.

Architecture d'un client


L'architecture d'un programme rseau demandant les services d'une application serveur sera la suivante :
ouvrir la connexion avec le service SB1 de la machine B
si russite alors
tant que ce n'est pas fini
prparer une demande
l'mettre vers la machine B
attendre et rcuprer la rponse
la traiter
fin tant que
finsi

Architecture d'un serveur


L'architecture d'un programme offrant des services sera la suivante :
50

ouvrir le service sur la machine locale


tant que le service est ouvert
se mettre l'coute des demandes de connexion sur un port dit port d'coute
lorsqu'il y a une demande, la faire traiter par une autre tche sur un autre port dit port de
service
fin tant que

Le programme serveur traite diffremment la demande de connexion initiale d'un client de ses demandes
ultrieures visant obtenir un service. Le programme n'assure pas le service lui-mme. S'il le faisait,
pendant la dure du service il ne serait plus l'coute des demandes de connexion et des clients ne seraient
alors pas servis. Il procde donc autrement : ds qu'une demande de connexion est reue sur le port
d'coute puis accepte, le serveur cre une tche charge de rendre le service demand par le client. Ce
service est rendu sur un autre port de la machine serveur appel port de service. On peut ainsi servir
plusieurs clients en mme temps.
Une tche de service aura la structure suivante :
tant que le service n'a pas t rendu totalement
attendre une demande sur le port de service
lorsqu'il y en a une, laborer la rponse
transmettre la rponse via le port de service
fin tant que
librer le port de service

La classe Socket
L'outil de base utilis par les programmes communiquant sur Internet est la socket. Ce mot anglais signifie
"prise de courant". Il est tendu ici pour signifier "prise de rseau". Pour qu'une application puisse envoyer
et recevoir des informations sur le rseau Internet, il lui faut une prise de rseau, une socket. Cet outil a t
initialement cr dans les versions d'Unix de l'universit de Berkeley. Il a t port depuis sur tous les
systmes Unix ainsi que dans le monde Windows. Il existe galement sur les machines virtuelles Java sous
deux formes : la classe Socket pour les applications clientes et la classe ServerSocket pour les applications
serveur.
Nous explicitons ici quelques-uns des constructeurs et mthodes de la classe Socket :
public Socket(String host, int port)

ouvre une connexion distante avec le port port de la machine host

public int getLocalPort()

rend le n du port local utilis par la socket

public int getPort()

rend le n du port distant auquel la socket est connecte

public InetAddress
getLocalAdress()

rend l'adresse InetAddress locale laquelle la socket est lie

public InetAddress
getInetAdress()

rend l'adresse InetAddress distante laquelle la socket est lie

public InputStream
getInputStream()

rend un flux d'entre permettant de lire les donnes envoyes par


le partenaire distant

public OutputStream
getOutputStream()

rend un flux de sortie permettant d'envoyer des donnes au


partenaire distant

public void shutdownInput()

ferme le flux d'entre de la socket

public void shutdownOutput()

ferme le flux de sortie de la socket

public void close()

ferme la socket et ses flux d'E/S

public String toString()

rend une chane de caractres "reprsentant" la socket

51

Ouverture d'une connexion avec une machine Serveur


Nous avons vu que pour qu'une machine A ouvre une connexion avec un service d'une machine B, il lui
fallait deux informations :
l'adresse IP ou le nom de la machine B
le numro de port o officie le service dsir
Le constructeur
public Socket(String host, int port);

cre une socket et la connecte la machine host sur le port port. Ce constructeur gnre une exception
dans diffrents cas :
- mauvaise adresse
- mauvais port
- demande refuse
-
Il nous faut grer cette exception :
Socket sClient=null;
try{
sClient=new Socket(host,port);
} catch(Exception e){
// la connexion a chou - on traite l'erreur
.
}

Si la demande de connexion russit, le client se voit localement attribuer un port pour communiquer avec
la machine B. Une fois la connexion tablie, on peut connatre ce port avec la mthode :
public int getLocalPort();

Si la connexion russit, nous avons vu que, de son ct, le serveur fait assurer le service par une autre
tche travaillant sur un port dit de service. Ce numro de port peut tre connu avec la mthode :
public int getPort();

Envoyer des informations sur le rseau


On peut obtenir un flux d'criture sur la socket et donc sur le rseau avec la mthode :
public OutputStream getOutputStream();

Tout ce qui sera envoy dans ce flux sera reu sur le port de service de la machine serveur. De nombreuses
applications ont un dialogue sous forme de lignes de texte termines par un passage a ligne. Aussi la
mthode println est-elle bien pratique dans ces cas l. On transforme alors le flux de sortie OutputStream
en flux PrintWriter qui possde la mthode println. L'criture peut gnrer une exception.
Lire des informations venant du rseau
On peut obtenir un flux de lecture des informations arrivant sur la socket avec la mthode :
public InputStream getInputStream();

Tout ce qui sera lu dans ce flux vient du port de service de la machine serveur. Pour les applications ayant
un dialogue sous forme de lignes de texte termines par un passage la ligne on aimera utiliser la mthode
readLine. Pour cela on transforme le flux d'entre InputStream en flux BufferedReader qui possde la
mthode readLine(). La lecture peut gnrer une exception.
Fermeture de la connexion
Elle se fait avec la mthode :
public void close();

La mthode peut gnrer une exception. Les ressources utilises, notamment le port rseau, sont libres.
52

La classe ServerSocket
Cette classe est destine la gestion des sockets cot serveur. Nous explicitons ici quelques-uns des
constructeurs et mthodes de cette classe :
public ServerSocket(int port)

cre une socket d'coutesur le port port

public ServerSocket(int port, int count)

idem mais fixe count la taille de la file d'attente, c.a.d. le


nombre maximal de connexions clientes mises en attente
si le serveur est occup lorsque la connexion cliente
arrive.

public int getLocalPort()

rend le n du port d'coute utilis par la socket

public InetAddress
getInetAdress()

rend l'adresse InetAddress locale laquelle la socket est lie

public Socket accept()

met le serveur en attente d'une connexion (opration,


bloquante). A l'arrive d'une connexion cliente, rend un
socket partir de laquelle sera rendu le service au client.

public void close()

ferme la socket et ses flux d'E/S

public String toString()

rend une chane de caractres "reprsentant" la socket

public void close()

ferme la socket de service et libre les ressources qui lui


sont associes

Ouverture du service
Elle se fait avec les deux constructeurs :
public ServerSocket(int port);
public ServerSocket(int port, int count);

port est le port d'coute du service : celui o les clients adressent leurs demandes de connexion. count est
la taille maximale de la file d'attente du service (50 par dfaut), celle-ci stockant les demandes de
connexion des clients auxquelles le serveur n'a pas encore rpondu. Lorsque la file d'attente est pleine, les
demandes de connexion qui arrivent sont rejetes. Les deux constructeurs gnrent une exception.
Acceptation d'une demande de connexion
Lorsq'un client fait une demande de connexion sur le port d'coute du service, celui-ci l'accepte avec la
mthode :
public Socket accept();

Cette mthode rend une instance de Socket : c'est la socket de service, celle travers laquelle le service
sera rendu, le plus souvent par une autre tche. La mthode peut gnrer une exception.
Lecture/Ecriture via la socket de service
La socket de service tant une instance de la classe Socket, on se reportera aux sections prcdentes o ce
sujet a t trait.
Identifier le client
Une fois la socket de service obtenue, le client peut tre identifi avec la mthode
public InetAddress getInetAddress()

de la classe Socket. On aura alors accs l'adresse IP et au nom du client.


53

Fermer le service
Cela se fait avec la mthode
public void close();

de la classe ServerSocket. Cela libre les ressources occupes, notamment le port d'coute. La mthode
peut gnrer une exception.

Applications
Serveur d'cho
Nous nous proposons d'crire un serveur d'cho qui sera lanc depuis une fentre DOS par la commande :
java serveurEcho port
Le serveur officie sur le port pass en paramtre. Il se contente de renvoyer au client la demande que
celui-ci lui a envoye accompagne de son identit (IP+nom). Il accepte 2 connexions dans sa liste
d'attente. On a l tous les constituants d'un serveur tcp. Le programme est le suivant :
// appel : serveurEcho port
// serveur d'cho
// renvoie au client la ligne que celui-ci lui a envoye
import java.net.*;
import java.io.*;
public class serveurEcho{
public final static String syntaxe="Syntaxe : serveurEcho port";
public final static int nbConnexions=2;
// programme principal
public static void main (String arg[]){
// y-a-t-il un argument
if(arg.length != 1)
erreur(syntaxe,1);
// cet argument doit tre entier >0
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(arg[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// on cre la socket d'coute
ServerSocket ecoute=null;
try{
ecoute=new ServerSocket(port,nbConnexions);
} catch (Exception e){
erreur("Erreur lors de la cration de la socket d'coute ("+e+")",3);
}
// suivi
System.out.println("Serveur d'cho lanc sur le port " + port);
// boucle de service
boolean serviceFini=false;
Socket service=null;
while (! serviceFini){
// attente d'un client
try{
service=ecoute.accept();
} catch (IOException e){
erreur("Erreur lors de l'acceptation d'une connexion ("+e+")",4);

54

}
// on identifie la liaison
try{
System.out.println("Client ["+identifie(service.getInetAddress())+","+
service.getPort()+"] connect au serveur [" + identifie (InetAddress.getLocalHost())
+ "," + service.getLocalPort() + "]");
} catch (Exception e) {
erreur("identification liaison",1);
}
// le service est assur par une autre tche
new traiteClientEcho(service).start();
}// fin while
}// fin main
// affichage des erreurs
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
// identifie
private static String identifie(InetAddress Host){
// identification de Host
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// fin class
// assure le service un client du serveur d'cho
class traiteClientEcho extends Thread{
private Socket service; // socket de service
private BufferedReader in; // flux d'entre
private PrintWriter out; // flux de sortie
// constructeur
public traiteClientEcho(Socket service){
this.service=service;
}
// mthode run
public void run(){
// cration des flux d'entre et de sortie
try{
in=new BufferedReader(new InputStreamReader(service.getInputStream()));
} catch (IOException e){
erreur("Erreur lors de la cration du flux dentre de la socket de service ("+e+")",1);
}// fin try
try{
out=new PrintWriter(service.getOutputStream(),true);
} catch (IOException e){
erreur("Erreur lors de la cration du flux de sortie de la socket de service ("+e+")",1);
}// fin try
// l'identification de la liaison est envoye au client
try{
out.println("Client ["+identifie(service.getInetAddress())+","+
service.getPort()+"] connect au serveur [" + identifie (InetAddress.getLocalHost())
+ "," + service.getLocalPort() + "]");
} catch (Exception e) {
erreur("identification liaison",1);
}
// boucle lecture demande/criture rponse
String demande,reponse;
try{
// le service s'arrte lorsque le client envoie une marque de fin de fichier
while ((demande=in.readLine())!=null){
// cho de la demande
reponse="["+demande+"]";
out.println(reponse);
// le service s'arrte lorsque le client envoie "fin"
if(demande.trim().toLowerCase().equals("fin")) break;
}// fin while
} catch (IOException e){

55

erreur("Erreur lors des changes client/serveur ("+e+")",3);


}// fin try
// on ferme la socket
try{
service.close();
} catch (IOException e){
erreur("Erreur lors de la fermeture de la socket de service ("+e+")",2);
}// fin try
}// fin run
// affichage des erreurs
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fin erreur
// identifie
private String identifie(InetAddress Host){
// identification de Host
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// fin class

Un client java pour le serveur d'cho


Dans la partie prcdente, nous avons utilis un client telnet pour tester le service d'cho. Nous crivons
maintenant notre propre client :
// appel : clientEcho machine port
// client du serveur d'cho
// envoie des lignes au serveur qui les lui renvoie en cho
import java.net.*;
import java.io.*;
public class clientEcho{
public final static String syntaxe="Syntaxe : clientEcho machine port";
// programme principal
public static void main (String arg[]){
// y-a-t-il deux arguments
if(arg.length != 2)
erreur(syntaxe,1);
// le premier argument doit tre le nom d'une machine existante
String machine=arg[0];
InetAddress serveurAddress=null;
try{
serveurAddress=InetAddress.getByName(machine);
} catch (Exception e){
erreur(syntaxe+"\nMachine "+machine+" inaccessible (" + e +")",2);
}
// le port doit tre entier >0
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(arg[1]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\nPort incorrect ("+E+")",3);
// on se connecte au serveur
Socket sClient=null;
try{
sClient=new Socket(machine,port);
} catch (Exception e){
erreur("Erreur lors de la cration de la socket de communication ("+e+")",4);
}
// on identifie la liaison

56

try{
System.out.println("Client : Client ["+identifie(InetAddress.getLocalHost())+","+
sClient.getLocalPort()+"] connect au serveur [" + identifie (sClient.getInetAddress())
+ "," + sClient.getPort() + "]");
} catch (Exception e) {
erreur("identification liaison ("+e+")",5);
}
// cration du flux de lecture des lignes tapes au clavier
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
erreur("Cration du flux d'entre clavier ("+e+")",6);
}
// cration du flux d'entre associe la socket client
BufferedReader in=null;
try{
in=new BufferedReader(new InputStreamReader(sClient.getInputStream()));
} catch (Exception e){
erreur("Cration du flux d'entre de la socket client("+e+")",7);
}
// cration du flux de sortie associe la socket client
PrintWriter out=null;
try{
out=new PrintWriter(sClient.getOutputStream(),true);
} catch (Exception e){
erreur("Cration du flux de sortie de la socket ("+e+")",8);
}
// boucle demandes - rponses
boolean serviceFini=false;
String demande=null;
String reponse=null;
// on lit le message envoy par le serveur juste aprs la connexion
try{
reponse=in.readLine();
} catch (IOException e){
erreur("Lecture rponse ("+e+")",4);
}
// affichage rponse
System.out.println("Serveur : " +reponse);
while (! serviceFini){
// lecture d'une ligne tape au clavier
System.out.print("Client : ");
try{
demande=IN.readLine();
} catch (Exception e){
erreur("Lecture ligne ("+e+")",9);
}
// envoi demande sur le rseau
try{
out.println(demande);
} catch (Exception e){
erreur("Envoi demande ("+e+")",10);
}
// attente/lecture rponse
try{
reponse=in.readLine();
} catch (IOException e){
erreur("Lecture rponse ("+e+")",4);
}
// affichage rponse
System.out.println("Serveur : " +reponse);
// est-ce fini ?
if(demande.trim().toLowerCase().equals("fin")) serviceFini=true;
}
// c'est fini
try{
sClient.close();
} catch(Exception e){
erreur("Fermeture socket ("+e+")",11);
}

57

}// main
// affichage des erreurs
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
// identifie
private static String identifie(InetAddress Host){
// identification de Host
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// fin class

La structure de ce client est conforme l'architecture gnrale des clients tcp. Ici, on a gr les diffrentes
exceptions possibles, une par une, ce qui alourdit le programme. Voici les rsultats obtenus lorsqu'on teste
ce client :
Client : Client [127.0.0.1,tahe,1045] connect au serveur [127.0.0.1,localhost,187]
Serveur : Client [127.0.0.1,localhost,1045] connect au serveur [127.0.0.1,tahe,187]
Client : 123
Serveur : [123]
Client : abcd
Serveur : [abcd]
Client : je suis l
Serveur : [je suis l]
Client : fin
Serveur : [fin]

Les lignes commenant par Client sont les lignes envoyes par le client et celles commenant par Serveur
sont celles que le serveur a renvoyes en cho.

Un client TCP gnrique


Beaucoup de services crs l'origine de l'Internet fonctionnent selon le modle du serveur d'cho tudi
prcdemment : les changes client-serveur se font pas changes de lignes de texte. Nous allons crire un
client tcp gnrique qui sera lanc de la faon suivante : java cltTCPgenerique serveur port
Ce client TCP se connectera sur le port port du serveur serveur. Ceci fait, il crera deux threads :
1. un thread charg de lire des commandes tapes au clavier et de les envoyer au serveur
2. un thread charg de lire les rponses du serveur et de les afficher l'cran
Pourquoi deux threads alors que dans l'application prcdente ce besoin ne s'tait pas fait ressentir ? Dans
cette dernire, le protocole du dialogue tait connu : le client envoyait une seule ligne et le serveur
rpondait par une seule ligne. Chaque service a son protocole particulier et on trouve galement les
situations suivantes :
le client doit envoyer plusieurs lignes de texte avant d'avoir une rponse
la rponse d'un serveur peut comporter plusieurs lignes de texte
Le programme du client tcp gnrique est le suivant :
// paquetages imports
import java.io.*;
import java.net.*;
public class clientTCPgenerique{
// reoit en paramtre les caractristiques d'un service sous la forme
// serveur port
// se connecte au service

58

// cre un thread pour lire des commandes tapes au clavier


// celles-ci seront envoyes au serveur
// cre un thread pour lire les rponses du serveur
// celles-ci seront affiches l'cran
// le tout se termine avec la commande fin tape au clavier
// variable d'instance
private static Socket client;
public static void main(String[] args){
// syntaxe
final String syntaxe="pg serveur port";
// nombre d'arguments
if(args.length != 2)
erreur(syntaxe,1);
// on note le nom du serveur
String serveur=args[0];
// le port doit tre entier >0
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[1]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
client=null;
// il peut y avoir des problmes
try{
// on se connecte au service
client=new Socket(serveur,port);
}catch(Exception ex){
// erreur
erreur("Impossible de se connecter au service ("+ serveur
+","+port+"), erreur : "+ex.getMessage(),3);
// fin
return;
}//catch
// on cre les threads de lecture/criture
new ClientSend(client).start();
new ClientReceive(client).start();
// fin thread main
return;
}// main
// affichage des erreurs
public static void erreur(String msg, int exitCode){
// affichage erreur
System.err.println(msg);
// arrt avec erreur
System.exit(exitCode);
}//erreur
}//classe
class ClientSend extends Thread {
// classe charge de lire des commandes tapes au clavier
// et de les envoyer un serveur via un client tcp pass en paramtre
private Socket client; // le client tcp
// constructeur
public ClientSend(Socket client){
// on note le client tcp
this.client=client;
}//constructeur
// mthode Run du thread
public void run(){
// donnes locales
PrintWriter OUT=null; // flux d'criture rseau
BufferedReader IN=null; // flux clavier
String commande=null; // commande lue au clavier
// gestion des erreurs
try{

59

// cration du flux d'criture rseau


OUT=new PrintWriter(client.getOutputStream(),true);
// cration du flux d'entre clavier
IN=new BufferedReader(new InputStreamReader(System.in));
// boucle saisie-envoi des commandes
System.out.println("Commandes : ");
while(true){
// lecture commande tape au clavier
commande=IN.readLine().trim();
// fini ?
if (commande.toLowerCase().equals("fin")) break;
// envoi commande au serveur
OUT.println(commande);
// commande suivante
}//while
}catch(Exception ex){
// erreur
System.err.println("Envoi : L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// fin - on ferme les flux
try{
OUT.close();client.close();
}catch(Exception ex){}
// on signale la fin du thread
System.out.println("[Envoi : fin du thread d'envoi des commandes au serveur]");
}//run
}//classe
class ClientReceive extends Thread{
// classe charge de lire les lignes de texte destines un
// client tcp pass en paramtre
private Socket client; // le client tcp
// constructeur
public ClientReceive(Socket client){
// on note le client tcp
this.client=client;
}//constructeur
// mthode Run du thread
public void run(){
// donnes locales
BufferedReader IN=null; // flux lecture rseau
String rponse=null; // rponse serveur
// gestion des erreurs
try{
// cration du flux lecture rseau
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
// boucle lecture lignes de texte du flux IN
while(true){
// lecture flux rseau
rponse=IN.readLine();
// flux ferm ?
if(rponse==null) break;
// affichage
System.out.println("<-- "+rponse);
}//while
}catch(Exception ex){
// erreur
System.err.println("Rception : L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// fin - on ferme les flux
try{
IN.close();client.close();
}catch(Exception ex){}
// on signale la fin du thread
System.out.println("[Rception : fin du thread de lecture des rponses du serveur]");
}//run
}//classe

Un serveur Tcp gnrique


Maintenant nous nous intressons un serveur
60

qui affiche l'cran les commandes envoyes par ses clients


leur envoie comme rponse les lignes de texte tapes au clavier par un utilisateur. C'est donc ce
dernier qui fait office de serveur.

Le programme est lanc par : java serveurTCPgenerique portEcoute, o portEcoute est le port sur
lequel les clients doivent se connecter. Le service au client sera assur par deux threads :
un thread se consacrant exclusivement la lecture des lignes de texte envoyes par le client
un thread se consacrant exclusivement la lecture des rponses tapes au clavier par l'utilisateur.
Celui-ci signalera par la commande fin qu'il clt la connexion avec le client.
Le serveur cre deux threads par client. S'il y a n clients, il y aura 2n threads actifs en mme temps. Le
serveur lui ne s'arrte jamais sauf par un Ctrl-C tap au clavier par l'utilisateur.
Le code du serveur tcp gnrique est le suivant :
// paquetages
import java.io.*;
import java.net.*;
public class serveurTCPgenerique{
// programme principal
public static void main (String[] args){
// reoit le port d'coute des demandes des clients
// cre un thread pour lire les demandes du client
// celles-ci seront affiches l'cran
// cre un thread pour lire des commandes tapes au clavier
// celles-ci seront envoyes comme rponse au client
// le tout se termine avec la commande fin tape au clavier
final String syntaxe="Syntaxe : pg port";
// variable d'instance
// y-a-t-il un argument
if(args.length != 1)
erreur(syntaxe,1);
// le port doit tre entier >0
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// on cre le servive d'coute
ServerSocket ecoute=null;
int nbClients=0; // nbre de clients traits
try{
// on cre le service
ecoute=new ServerSocket(port);
// suivi
System.out.println("Serveur gnrique lanc sur le port " + port);
// boucle de service aux clients
Socket client=null;
while (true){ // boucle infinie - sera arrte par Ctrl-C
// attente d'un client
client=ecoute.accept();
// le service est assur des threads spars
nbClients++;
// on cre les threads de lecture/criture
new ServeurSend(client,nbClients).start();
new ServeurReceive(client,nbClients).start();
// on retourne l'coute des demandes
}// fin while
}catch(Exception ex){
// on signale l'erreur

61

erreur("L'erreur suivante s'est produite : " + ex.getMessage(),3);


}//catch
}// fin main
// affichage des erreurs
public static void erreur(String msg, int exitCode){
// affichage erreur
System.err.println(msg);
// arrt avec erreur
System.exit(exitCode);
}//erreur
}//classe
class ServeurSend extends Thread{
// classe charge de lire des rponses tapes au clavier
// et de les envoyer un client via un client tcp pass au constructeur
Socket client; // le client tcp
int numClient; // n de client
// constructeur
public ServeurSend(Socket client, int numClient){
// on note le client tcp
this.client=client;
// et son n
this.numClient=numClient;
}//constructeur
// mthode Run du thread
public void run(){
// donnes locales
PrintWriter OUT=null; // flux d'criture rseau
String rponse=null; // rponse lue au clavier
BufferedReader IN=null; // flux clavier
// suivi
System.out.println("Thread de lecture des rponses du serveur au client "+ numClient + " lanc");
// gestion des erreurs
try{
// cration du flux d'criture rseau
OUT=new PrintWriter(client.getOutputStream(),true);
// cration du flux clavier
IN=new BufferedReader(new InputStreamReader(System.in));
// boucle saisie-envoi des commandes
while(true){
// identification client
System.out.print("--> " + numClient + " : ");
// lecture rponse tape au clavier
rponse=IN.readLine().trim();
// fini ?
if (rponse.toLowerCase().equals("fin")) break;
// envoi rponse au serveur
OUT.println(rponse);
// rponse suivante
}//while
}catch(Exception ex){
// erreur
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// fin - on ferme les flux
try{
OUT.close();client.close();
}catch(Exception ex){}
// on signale la fin du thread
System.out.println("[fin du Thread de lecture des rponses du serveur au client "+ numClient+
"]");
}//run
}//classe
class ServeurReceive extends Thread{
// classe charge de lire les lignes de texte envoyes au serveur
// via un client tcp pass au constructeur
Socket client; // le client tcp
int numClient; // n de client
// constructeur
public ServeurReceive(Socket client, int numClient){
// on note le client tcp
this.client=client;

62

// et son n
this.numClient=numClient;
}//constructeur
// mthode Run du thread
public void run(){
// donnes locales
BufferedReader IN=null; // flux lecture rseau
String rponse=null; // rponse serveur
// suivi
System.out.println("Thread de lecture des demandes du client "+ numClient + " lanc");
// gestion des erreurs
try{
// cration du flux lecture rseau
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
// boucle lecture lignes de texte du flux IN
while(true){
// lecture flux rseau
rponse=IN.readLine();
// flux ferm ?
if(rponse==null) break;
// affichage
System.out.println("<-- "+rponse);
}//while
}catch(Exception ex){
// erreur
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// fin - on ferme les flux
try{
IN.close();client.close();
}catch(Exception ex){}
// on signale la fin du thread
System.out.println("[fin du Thread de lecture des demandes du client "+ numClient+"]");
}//run
}//classe

63

Dfinir un package
import permet d'importer n'importe quelle classe d'une bibliothque, mais vous pouvez aussi crer votre
propre bibliothque, pour y rassembler par exemple un groupe de classes utilises comme outils dans un
ou plusieurs projets. Ceci se fait trs simplement grce la clause package. Si cette clause est utilise, elle
doit tre dfinie en tte d'un fichier .java , comme suit :
package nomPackage;
Comme expliqu prcdemment, le nom de package doit correspondre au chemin d'accs la classe qui
utilise la clause package.

Classes et Objets
Les classes Java peuvent possder des mthodes et des attributs.
Les mthodes dfinissent les actions quune classe peut effectuer.
Les attributs dcrivent la classe.
Aprs le mot classe, tu dois maintenant thabituer la nouvelle signification du mot objet.
La phrase "crer une instance dun objet" signifie crer une copie de cet objet dans la mmoire de
lordinateur en respectant la dfinition de sa classe.
La dclaration d'une classe peut prendre une des formes suivantes :
// Dclararation d'une classe simple
ModifieurDeClasse class NomDeClasse
{
// Corps de NomDeClasse :
// Dclaration des champs, des mthodes, des constructeurs
// et/ou initialisations static
}
// Dclaration d'une classe drivant d'une super classe
ModifieurDeClasse class NomDeClasseDerivee extends NomDeSuperClasse
{
// Corps de NomDeClasseDerivee :
// Dclaration des champs, des mthodes, des constructeurs
// et/ou initialisations static
}
// Dclaration d'une classe implmentant une interface
ModifieurDeClasse class NomDeClasse2 implements NomInterface //, NomInterface2, ...
{
// Corps de NomDeClasse2 :
// Dclaration des champs, des mthodes, des constructeurs
// et/ou initialisations static
// et implmentation des mthodes de nomInterface
}
// Dclaration d'une classe drivant d'une super classe et implmentant une interface
ModifieurDeClasse class NomDeClasse3 extends NomDeSuperClasse
implements NomInterface //, NomInterface2, ...
{
// Corps de NomDeClasse3 :
// Dclaration des champs, des mthodes, des constructeurs
// et/ou initialisations static
// et implmentation des mthodes de nomInterface
}

Une classe simple drive implicitement de la classe Object (class nomDeClasse est quivalent class
nomDeClasse extends Object). Java ne permet pas l'hritage multiple (une seule classe peut suivre la
clause extends), mais une classe peut implmenter plusieurs interfaces.
64

Le corps d'une classe est une suite quelconque de dclaration de champs, de mthodes, de constructeurs
et/ou d'initialisations static.
ModifieurDeClasse est optionnel et peut prendre une ou plusieurs des valeurs suivantes :

public : Une seule classe ou interface peut tre dclare public par fichier source .java . Par
convention, le fichier porte le nom de la classe dclare public. Si d'autres classes (non public)
sont dclares dans un fichier Classe1.java, elles ne peuvent tre utiliss que dans les fichiers qui
appartiennent au mme package que Classe1.java.
final : Une classe dclare final ne peut tre drive et ne peut donc jamais suivre la clause
extends. Cette clause peut tre utile quand vous considrez qu'une classe ne doit pas ou n'a pas
besoin d'tre drive.
abstract : Il est impossible de crer une instance d'une classe dclare abstract. Cette catgorie de
classe peut comporter une ou plusieurs mthodes dclares abstract. Par contre, si une classe
comporte une mthode abstract, elle doit tre dclare abstract. A quoi sert une classe abstract si
on ne peut crer aucun objet de cette classe ? Ce type de classe est utilis pour fournir des
mthodes et des champs communs toutes les classes qui en drivent. L'intrt des classes
abstract est dmontr plus loin dans ce chapitre. Une classe ne peut tre dclare abstract et final
(elle ne servirait rien puisqu'il serait impossible de crer des classes drives de celle-ci).

Les interfaces
Une interface est une catgorie un peu spciale de classe abstract, dont le corps ne contient que la
dclaration de constantes et de mthodes abstract :
// Dclararation d'une interface simple
ModifieurInterface interface NomInterface
{
// Corps de NomInterface :
// Dclaration des constantes et des mthodes non implmentes
}
// Dclaration d'une interface drivant d'une super interface
ModifieurInterface
interface
NomInterfaceDerivee
extends
NomSuperInterfa
{
// Corps de NomInterfaceDerivee :
// Dclaration des constantes et des mthodes non implmentes
}

NomSuperInterface

//,

II est impossible de crer une instance d'une interface. Une ou plusieurs interfaces peuvent suivre la clause
extends.

65

Vous aimerez peut-être aussi