Vous êtes sur la page 1sur 871

Livre Java .book Page I Jeudi, 25.

novembre 2004 3:04 15

Livre Java .book Page I Jeudi, 25. novembre 2004 3:04 15

Au cur de Java 2
volume 1
Notions fondamentales
Cay S. Horstmann
et Gary Cornell

Livre Java .book Page II Jeudi, 25. novembre 2004 3:04 15

CampusPress a apport le plus grand soin la ralisation de ce livre afin de vous fournir une information complte et fiable. Cependant, CampusPress nassume de responsabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces personnes qui pourraient
rsulter de cette utilisation.
Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les descriptions thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou professionnelle.
CampusPress ne pourra en aucun cas tre tenu pour responsable des prjudices ou dommages de
quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou programmes.
Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs
propritaires respectifs.

Publi par CampusPress


47 bis, rue des Vinaigriers
75010 PARIS
Tl. : 01 72 74 90 00

Titre original : Core Java 2, volume 1 Fundamentals


Traduit de lamricain par :
Christiane Silhol et Nathalie Le Guillou de Penanros

Mise en pages : TyPAO


ISBN : 2-7440-1833-3
Copyright 2004 CampusPress
Tous droits rservs
CampusPress est une marque
de Pearson Education France

ISBN original : 0-13-148202-5


Copyright 2005 Sun Microsystems, Inc.
Tous droits rservs
Sun Microsystems Inc.
901 San Antonio Road, Palo Alto, California
94303 USA

Toute reproduction, mme partielle, par quelque procd que ce soit, est interdite sans autorisation pralable. Une copie par
xrographie, photographie, film, support magntique ou autre, constitue une contrefaon passible des peines prvues par la
loi, du 11 mars 1957 et du 3 juillet 1995, sur la protection des droits dauteur.

Livre Java .book Page III Jeudi, 25. novembre 2004 3:04 15

Table des matires

Introduction .................................................................................................................................

Avertissement au lecteur ..........................................................................................................


A propos de ce livre .................................................................................................................
Conventions .............................................................................................................................
Exemples de code ....................................................................................................................

1
3
5
5

Chapitre 1. Une introduction Java .........................................................................................

Java, plate-forme de programmation .......................................................................................


Les termes cls du livre blanc de Java .....................................................................................
Simplicit ...........................................................................................................................
Orient objet .......................................................................................................................
Distribu .............................................................................................................................
Fiabilit ..............................................................................................................................
Scurit ...............................................................................................................................
Architecture neutre .............................................................................................................
Portabilit ...........................................................................................................................
Interprt ............................................................................................................................
Performances leves .........................................................................................................
Multithread .........................................................................................................................
Java, langage dynamique ....................................................................................................
Java et Internet .........................................................................................................................
Bref historique de Java .............................................................................................................
Les ides fausses les plus rpandues concernant Java .............................................................

7
8
8
9
10
10
10
11
12
12
12
13
13
14
15
18

Chapitre 2. Lenvironnement de programmation de Java .....................................................

23

Installation du kit de dveloppement Java ...............................................................................


Tlcharger le JDK ............................................................................................................

23
24

Livre Java .book Page IV Jeudi, 25. novembre 2004 3:04 15

IV

Table des matires

Configurer le chemin dexcution ......................................................................................


Installer la bibliothque et la documentation .....................................................................
Installer les exemples de programmes ...............................................................................
Explorer les rpertoires de Java .........................................................................................
Choix de lenvironnement de dveloppement .........................................................................
Utilisation des outils de ligne de commande ...........................................................................
Conseils pour la recherche derreurs ..................................................................................
Utilisation dun environnement de dveloppement intgr .....................................................
Localiser les erreurs de compilation ..................................................................................
Compilation et excution de programmes partir dun diteur de texte .................................
Excution dune application graphique ...................................................................................
Elaboration et excution dapplets ...........................................................................................

25
26
26
27
28
29
30
32
34
35
37
39

Chapitre 3. Structures fondamentales de la programmation Java .......................................


Un exemple simple de programme Java ..................................................................................
Commentaires ..........................................................................................................................
Types de donnes .....................................................................................................................
Entiers ................................................................................................................................
Types virgule flottante .....................................................................................................
Le type char .......................................................................................................................
Type boolen ......................................................................................................................
Variables ..................................................................................................................................
Initialisation des variables ......................................................................................................
Constantes ..........................................................................................................................
Oprateurs ................................................................................................................................
Oprateurs dincrmentation et de dcrmentation ...........................................................
Oprateurs relationnels et boolens ...................................................................................
Oprateurs binaires ............................................................................................................
Fonctions mathmatiques et constantes .............................................................................
Conversions de types numriques ......................................................................................
Transtypages ......................................................................................................................
Parenthses et hirarchie des oprateurs ............................................................................
Types numrs ..................................................................................................................
Chanes ....................................................................................................................................
Points et units de code ......................................................................................................
Sous-chanes .......................................................................................................................
Modification de chanes .....................................................................................................
Concatnation .....................................................................................................................
Test dgalit des chanes ...................................................................................................
Lire la documentation API en ligne ...................................................................................

43
44
47
47
48
49
50
52
52
53
54
54
56
56
57
58
59
60
60
61
62
62
63
63
65
65
68

Livre Java .book Page V Jeudi, 25. novembre 2004 3:04 15

Table des matires

Entres et sorties ......................................................................................................................


Lire les caractres entrs ....................................................................................................
Mise en forme de laffichage ..............................................................................................
Flux dexcution ......................................................................................................................
Porte dun bloc .................................................................................................................
Instructions conditionnelles ...............................................................................................
Boucles ..............................................................................................................................
Boucles dtermines ..........................................................................................................
Slections multiples linstruction switch .......................................................................
Interrompre le flux dexcution ..........................................................................................
Grands nombres .......................................................................................................................
Tableaux ...................................................................................................................................
La boucle "for each" ...........................................................................................................
Initialiseurs de tableaux et tableaux anonymes ..................................................................
Copie des tableaux .............................................................................................................
Paramtres de ligne de commande .....................................................................................
Tri dun tableau ..................................................................................................................
Tableaux multidimensionnels .............................................................................................
Tableaux irrguliers ............................................................................................................

70
70
73
78
78
79
82
85
88
90
92
94
95
96
97
98
99
102
104

Chapitre 4. Objets et classes ......................................................................................................


Introduction la programmation oriente objet ......................................................................
Le vocabulaire de la POO ..................................................................................................
Les objets ...........................................................................................................................
Relations entre les classes ..................................................................................................
Comparaison entre POO et programmation procdurale traditionnelle .............................
Utilisation des classes existantes .............................................................................................
Objets et variables objet .....................................................................................................
La classe GregorianCalendar de la bibliothque Java .......................................................
Les mthodes daltration et les mthodes daccs ...........................................................
Construction de vos propres classes ........................................................................................
Une classe Employee ..........................................................................................................
Travailler avec plusieurs fichiers source ............................................................................
Analyser la classe Employee ..............................................................................................
Premiers pas avec les constructeurs ...................................................................................
Paramtres implicites et explicites .....................................................................................
Avantages de lencapsulation .............................................................................................
Privilges daccs fonds sur les classes ............................................................................
Mthodes prives ...............................................................................................................
Champs dinstance final .....................................................................................................

109
110
111
112
113
115
116
117
120
121
127
127
130
130
131
132
133
136
136
136

Livre Java .book Page VI Jeudi, 25. novembre 2004 3:04 15

VI

Table des matires

Champs et mthodes statiques .................................................................................................


Champs statiques ................................................................................................................
Constantes ..........................................................................................................................
Mthodes statiques .............................................................................................................
Mthodes "factory" ............................................................................................................
La mthode main ................................................................................................................
Paramtres des mthodes .........................................................................................................
Construction dun objet ..........................................................................................................
Surcharge ............................................................................................................................
Initialisation des champs par dfaut ...................................................................................
Constructeurs par dfaut ....................................................................................................
Initialisation explicite de champ ........................................................................................
Noms de paramtres ...........................................................................................................
Appel dun autre constructeur ............................................................................................
Blocs dinitialisation ..........................................................................................................
Destruction des objets et mthode finalize .........................................................................
Packages ..................................................................................................................................
Importation des classes ......................................................................................................
Imports statiques ................................................................................................................
Ajout dune classe dans un package ..................................................................................
Comment la machine virtuelle localise les classes ............................................................
Visibilit dans un package ..................................................................................................
Commentaires pour la documentation .....................................................................................
Insertion des commentaires ................................................................................................
Commentaires de classe .....................................................................................................
Commentaires de mthode .................................................................................................
Commentaires de champ ....................................................................................................
Commentaires gnraux .....................................................................................................
Commentaires de package et densemble ..........................................................................
Extraction des commentaires .............................................................................................
Conseils pour la conception de classes ....................................................................................

137
137
138
139
140
140
143
149
149
149
150
151
152
152
153
157
157
158
159
160
163
166
167
168
168
169
170
170
171
171
172

Chapitre 5. Lhritage ................................................................................................................


Classes, superclasses et sous-classes .......................................................................................
Hirarchie dhritage ..........................................................................................................
Polymorphisme ..................................................................................................................
Liaison dynamique .............................................................................................................
Empcher lhritage : les classes et les mthodes final ......................................................
Transtypage ........................................................................................................................
Classes abstraites ................................................................................................................
Accs protg .....................................................................................................................

175
176
182
183
184
187
188
190
195

Livre Java .book Page VII Jeudi, 25. novembre 2004 3:04 15

Table des matires

VII

Object : la superclasse cosmique .............................................................................................


La mthode equals .............................................................................................................
Test dgalit et hritage ....................................................................................................
La mthode hashCode .......................................................................................................
La mthode toString ...........................................................................................................
Listes de tableaux gnriques ..................................................................................................
Accder aux lments dune liste de tableaux ...................................................................
Compatibilit entre les listes de tableaux brutes et tapes .................................................
Enveloppes dobjets et autoboxing ..........................................................................................
Mthodes ayant un nombre variable de paramtres ...........................................................
Rflexion ..................................................................................................................................
La classe Class ...................................................................................................................
La rflexion pour analyser les caractristiques dune classe ..............................................
La rflexion pour lanalyse des objets lexcution ..........................................................
La rflexion pour crer un tableau gnrique .....................................................................
Les pointeurs de mthodes .................................................................................................
Classes dnumration .............................................................................................................
Conseils pour lutilisation de lhritage ...................................................................................

196
197
198
200
202
207
210
214
215
218
219
220
223
228
232
236
239
241

Chapitre 6. Interfaces et classes internes .................................................................................


Interfaces ..................................................................................................................................
Proprits des interfaces .....................................................................................................
Interfaces et classes abstraites ............................................................................................
Clonage dobjets ......................................................................................................................
Interfaces et callbacks ..............................................................................................................
Classes internes ........................................................................................................................
Accder ltat dun objet laide dune classe interne ...................................................
Rgles particulires de syntaxe pour les classes internes ...................................................
Utilit, ncessit et scurit des classes internes ................................................................
Classes internes locales ......................................................................................................
Classes internes anonymes .................................................................................................
Classes internes statiques ...................................................................................................
Proxies .....................................................................................................................................
Proprits des classes proxy ...............................................................................................

243
244
249
250
251
257
260
261
264
265
267
270
272
275
279

Chapitre 7. Programmation graphique ....................................................................................


Introduction Swing ................................................................................................................
Cration dun cadre ..................................................................................................................
Positionnement dun cadre ......................................................................................................
Affichage des informations dans un panneau ..........................................................................
Formes 2D ...............................................................................................................................

281
282
285
288
294
298

Livre Java .book Page VIII Jeudi, 25. novembre 2004 3:04 15

VIII

Table des matires

Couleurs ...................................................................................................................................
Remplir des formes ............................................................................................................
Texte et polices ........................................................................................................................
Images ......................................................................................................................................

306
309
311
319

Chapitre 8. Gestion des vnements .........................................................................................


Introduction la gestion des vnements ................................................................................
Exemple : gestion dun clic de bouton ...............................................................................
Etre confortable avec les classes internes ..........................................................................
Transformer des composants en couteurs dvnement ...................................................
Exemple : modification du "look and feel" ........................................................................
Exemple : capture des vnements de fentre ....................................................................
Hirarchie des vnements AWT .............................................................................................
Evnements smantiques et de bas niveau ...............................................................................
Rsum de la gestion des vnements ...............................................................................
Types dvnements de bas niveau ..........................................................................................
Evnements du clavier .......................................................................................................
Evnements de la souris .....................................................................................................
Evnements de focalisation ................................................................................................
Actions .....................................................................................................................................
Multidiffusion ..........................................................................................................................
Implmenter des sources dvnements ..................................................................................

325
326
328
333
336
338
341
345
347
349
350
350
356
365
368
377
380

Chapitre 9. Swing et les composants dinterface utilisateur ..................................................


Larchitecture Modle-Vue-Contrleur ...................................................................................
Une analyse Modle-Vue-Contrleur des boutons Swing .................................................
Introduction la gestion de mise en forme ..............................................................................
Gestionnaire BorderLayout ................................................................................................
Panneaux ............................................................................................................................
Disposition des grilles ........................................................................................................
Entre de texte .........................................................................................................................
Champs de texte .................................................................................................................
Etiquettes et composants dtiquetage ...............................................................................
Suivi des modifications dans les champs de texte ..............................................................
Champs de mot de passe ....................................................................................................
Champs de saisie mis en forme ..........................................................................................
Zones de texte ....................................................................................................................
Composants du choix ...............................................................................................................
Cases cocher ....................................................................................................................
Boutons radio .....................................................................................................................
Bordures .............................................................................................................................

385
386
390
392
394
396
397
401
402
403
405
410
410
426
431
431
434
437

Livre Java .book Page IX Jeudi, 25. novembre 2004 3:04 15

Table des matires

IX

Listes droulantes ...............................................................................................................


Curseurs .............................................................................................................................
Le composant JSpinner ......................................................................................................
Menus ......................................................................................................................................
Cration dun menu ............................................................................................................
Icnes et options de menu ..................................................................................................
Options de menu avec cases cocher et boutons radio .....................................................
Menus contextuels ..............................................................................................................
Caractres mnmoniques et raccourcis clavier ..................................................................
Activation et dsactivation des options de menu ...............................................................
Barres doutils ....................................................................................................................
Bulles daide ......................................................................................................................
Mise en forme sophistique .....................................................................................................
Gestionnaire BoxLayout .....................................................................................................
Gestionnaire GridBagLayout .............................................................................................
SpringLayout ......................................................................................................................
Cration sans gestionnaire de mise en forme .....................................................................
Gestionnaires de mise en forme personnaliss ..................................................................
Squence de tabulation .......................................................................................................
Botes de dialogue ....................................................................................................................
Botes de dialogue doptions ..............................................................................................
Cration de botes de dialogue ...........................................................................................
Echange de donnes ...........................................................................................................
Botes de dialogue Fichier ..................................................................................................
Slecteurs de couleur .........................................................................................................

442
445
451
459
460
463
464
465
467
469
473
475
478
481
486
496
506
507
511
512
513
524
528
534
547

Chapitre 10. Dployer des applets et des applications ...........................................................


Introduction aux applets ..........................................................................................................
Un petit applet ....................................................................................................................
Affichage des applets .........................................................................................................
Conversion dune application en applet .............................................................................
Le cycle de vie dun applet ................................................................................................
Premires rgles de scurit ...............................................................................................
Fentres pop-up dans un applet ..........................................................................................
Balises HTML et attributs pour applets ...................................................................................
Les attributs de positionnement dun applet ......................................................................
Les attributs dapplet pour la partie Code ..........................................................................
Les attributs dun applet pour les visualisateurs acceptant Java ........................................
La balise object ..................................................................................................................
Passer des informations un applet avec des paramtres ..................................................

553
554
556
557
559
561
562
564
566
567
568
570
571
571

Livre Java .book Page X Jeudi, 25. novembre 2004 3:04 15

Table des matires

Le multimdia ..........................................................................................................................
Encapsuler les URL ...........................................................................................................
Rcuprer des fichiers multimdias ...................................................................................
Le contexte dapplet .................................................................................................................
La communication interapplets ..........................................................................................
Faire afficher des informations par le navigateur ...............................................................
Un applet signet .................................................................................................................
Cest un applet et cest aussi une application ! ..................................................................
Les fichiers JAR .......................................................................................................................
Packaging des applications ......................................................................................................
Le manifeste .......................................................................................................................
Fichiers JAR auto-extractibles ...........................................................................................
Les ressources ....................................................................................................................
Verrouillage ........................................................................................................................
Java Web Start ..........................................................................................................................
LAPI JNLP .......................................................................................................................
Stockage des prfrences dapplications .................................................................................
Concordances de proprits ...............................................................................................
Informations systme .........................................................................................................
LAPI Preferences .............................................................................................................

576
576
577
578
578
579
581
583
589
591
591
592
593
597
597
600
611
611
615
617

Chapitre 11. Exceptions et mise au point .................................................................................

625

Le traitement des erreurs .........................................................................................................


Le classement des exceptions .............................................................................................
Signaler les exceptions sous contrle .................................................................................
Comment lancer une exception ..........................................................................................
Crer des classes dexception .............................................................................................
Capturer les exceptions ............................................................................................................
Capturer des exceptions multiples .....................................................................................
Relancer et enchaner les exceptions ..................................................................................
La clause finally .................................................................................................................
Analyser les traces de piles ................................................................................................
Un dernier mot sur la gestion des erreurs et des exceptions de Java .................................
Quelques conseils sur lutilisation des exceptions ...................................................................
La consignation ........................................................................................................................
Consignation de base .........................................................................................................
Consignation avance .........................................................................................................
Modifier la configuration du gestionnaire de journaux ......................................................
La localisation ....................................................................................................................

626
627
629
631
632
633
635
635
636
639
642
646
649
650
650
652
653

Livre Java .book Page XI Jeudi, 25. novembre 2004 3:04 15

Table des matires

XI

Les gestionnaires ................................................................................................................

654

Les filtres ............................................................................................................................

658

Les formateurs ....................................................................................................................

658

Les assertions ...........................................................................................................................

666

Activation et dsactivation des assertions ..........................................................................

667

Conseils dutilisation des assertions ..................................................................................

668

Les techniques de mise au point ..............................................................................................

670

Quelques tours de main pour le dbogage .........................................................................

670

Utiliser une fentre de console ...........................................................................................

676

Tracer les vnements AWT ...............................................................................................

678

Le robot awt .......................................................................................................................

681

Utiliser un dbogueur ..............................................................................................................

685

Le dbogueur JDB .............................................................................................................

685

Le dbogueur Eclipse .........................................................................................................

691

Chapitre 12. Les flux et les fichiers ...........................................................................................

693

Les flux ....................................................................................................................................

693

Lire et crire des octets ......................................................................................................

694

La faune des flux ......................................................................................................................

696

Empilements de flux filtrs ................................................................................................

700

Flux de donnes .................................................................................................................

704

Flux de fichiers en accs direct ..........................................................................................

707

Les flux de texte .................................................................................................................

708

Jeux de caractres ..............................................................................................................

709

La sortie du texte ................................................................................................................

718

Lentre de texte .................................................................................................................

720

Les flux de fichiers ZIP ............................................................................................................

721

Lutilisation des flux ................................................................................................................

729

Ecrire en format fixe ...........................................................................................................

729

Analyseurs lexicaux pour les textes dlimits ...................................................................

730

Lecture en format fixe ........................................................................................................

731

La classe StringBuilder ......................................................................................................

735

Les flux en accs direct ......................................................................................................

736

Les flux dobjets ......................................................................................................................

742

Ecrire des objets de types variables ...................................................................................

742

La srialisation des objets ..................................................................................................

746

Rsoudre le problme de lcriture des rfrences dobjets ..............................................

750

Livre Java .book Page XII Jeudi, 25. novembre 2004 3:04 15

XII

Table des matires

Comprendre le format de sortie des rfrences dobjets ....................................................


Modifier le mcanisme de srialisation par dfaut .............................................................
Srialisation des singletons et numrations sres ............................................................
La gestion des versions ......................................................................................................
La srialisation comme outil de clonage ............................................................................
La gestion des fichiers .............................................................................................................
Nouvelles E/S ..........................................................................................................................
Fichiers concordance de mmoire ...................................................................................
La structure des donnes du tampon ..................................................................................
Verrouillage des fichiers .....................................................................................................
Expressions ordinaires .............................................................................................................

756
758
760
761
763
766
771
772
778
780
782

Chapitre 13. Programmation gnrique ...................................................................................

793

Pourquoi la programmation gnrique ? ..................................................................................


Y a-t-il un programmeur gnrique dans la salle ? ............................................................
Dfinition dune classe gnrique simple ................................................................................
Mthodes gnriques ...............................................................................................................
Limites pour variables de type .................................................................................................
Code gnrique et machine virtuelle .......................................................................................
Traduire les expressions gnriques ...................................................................................
Traduire les mthodes gnriques ......................................................................................
Appeler un code existant ....................................................................................................
Restrictions et limites ..............................................................................................................
Types primitifs ...................................................................................................................
Informations sur le type dexcution ..................................................................................
Exceptions ..........................................................................................................................
Tableaux .............................................................................................................................
Instanciation de types gnriques ......................................................................................
Contextes statiques .............................................................................................................
Conflits aprs un effacement ..............................................................................................
Rgles dhritage pour les types gnriques ............................................................................
Types joker ...............................................................................................................................
Limites de supertypes pour les jokers ................................................................................
Jokers sans limites ..............................................................................................................
Capture de caractres joker ................................................................................................
Rflexion et gnrique .............................................................................................................
Utilisation des paramtres Class<T> pour la concordance de type ..................................
Informations de type gnrique dans la machine virtuelle ................................................

794
795
796
797
798
800
802
802
804
805
805
806
806
807
807
808
808
809
810
812
814
815
818
819
819

Livre Java .book Page XIII Jeudi, 25. novembre 2004 3:04 15

Table des matires

XIII

Annexe A. Les mots cls de Java ...............................................................................................

825

Annexe B. Adaptation en amont du code du JDK 5.0 ............................................................


Amlioration de la boucle for ..................................................................................................
Listes de tableaux gnriques ..................................................................................................
Autoboxing ..............................................................................................................................
Listes de paramtres variables .................................................................................................
Types de retour covariants .......................................................................................................
Importation statique .................................................................................................................
Saisie la console ....................................................................................................................
Sortie mise en forme ................................................................................................................
Dlgation du volet conteneur .................................................................................................
Points de code Unicode ...........................................................................................................
Construction des chanes .........................................................................................................

829
829
830
830
830
831
831
831
832
832
832
833

Index .............................................................................................................................................

835

Livre Java .book Page XIV Jeudi, 25. novembre 2004 3:04 15

Livre Java .book Page 1 Jeudi, 25. novembre 2004 3:04 15

Introduction
Avertissement au lecteur
Vers la fin de 1995, le langage de programmation Java surgit sur la grande scne dInternet et obtint
immdiatement un norme succs. La prtention de Java est de constituer la colle universelle capable de connecter les utilisateurs aux informations, que celles-ci proviennent de serveurs Web, de
bases de donnes, de fournisseurs dinformations ou de toute autre source imaginable. Et Java se
trouve en bonne position pour relever ce dfi. Il sagit dun langage de conception trs robuste qui a
t adopt par la majorit des principaux fournisseurs, lexception de Microsoft. Ses caractristiques intgres de scurit offrent un sentiment de confiance aux programmeurs comme aux utilisateurs des applications. De plus, Java intgre des fonctionnalits qui facilitent grandement certaines
tches de programmation avances, comme la gestion rseaux, la connectivit bases de donnes ou
le dveloppement dapplications multitches.
Depuis le lancement de Java, Sun Microsystems a mis six rvisions majeures du kit de dveloppement Java. Au cours des neuf dernires annes, lAPI (interface de programmation dapplication)
est passe de 2 000 plus de 3 000 classes. Elle traite maintenant des domaines aussi divers que
la construction de linterface utilisateur, la gestion des bases de donnes, linternationalisation, la
scurit et le traitement du code XML. Le JDK 5.0, sorti en 2004, constitue la mise jour la plus importante du langage Java depuis sa premire sortie.
Louvrage que vous tenez entre les mains est le premier volume de la septime dition de Au cur de
Java 2. Chaque dition a suivi la sortie du kit de dveloppement daussi prs que possible et,
chaque fois, nous avons rcrit le livre pour y inclure les toutes dernires fonctionnalits. Dans cette
dition, nous nous passionnons pour les collections gnriques, lamlioration de la boucle for et
dautres caractristiques passionnantes du JDK 5.0.
Ce livre, comme les ditions prcdentes, sadresse essentiellement aux programmeurs professionnels dsireux dutiliser Java pour dvelopper de vritables projets. Nous considrons que le lecteur
possde dj une solide habitude de la programmation, mais il na pas ncessairement besoin de
connatre le langage C++ ou la programmation oriente objet. Les programmeurs expriments qui
utilisent Visual Basic, C ou COBOL nprouveront pas de difficults comprendre le contenu de cet
ouvrage (il nest pas mme ncessaire que vous ayez une exprience de la programmation des interfaces graphiques sous Windows, UNIX ou Macintosh).
Nous supposons donc, a priori, que :
m

Vous souhaitez crire de vrais programmes permettant de rsoudre de vrais problmes.

Vous naimez pas les livres qui fourmillent dexemples dpourvus dutilit pratique.

Vous trouverez de nombreux programmes de dmonstration qui abordent la plupart des sujets traits
dans cet ouvrage. Ces programmes sont volontairement simples afin que le lecteur puisse se concentrer

Livre Java .book Page 2 Jeudi, 25. novembre 2004 3:04 15

Au cur de Java 2 - Notions fondamentales

sur les points importants. Nanmoins, dans la plupart des cas, il sagit dapplications utiles qui pourront
vous servir de base pour le dveloppement de vos propres projets.
Nous supposons galement que vous souhaitez apprendre les caractristiques avances de Java ;
cest pourquoi nous tudierons en dtail :
m

la programmation oriente objet ;

le mcanisme de rflexion de Java et de proxy ;

les interfaces et classes internes ;

le modle dcouteur dvnement ;

la conception dinterfaces graphiques avec la bote outils Swing ;

la gestion des exceptions ;

les flux dE/S et la srialisation dobjet ;

la programmation gnrique.

Enfin, compte tenu de lexplosion de la bibliothque de classes de Java, nous avons d rpartir
ltude de toutes les fonctionnalits sur deux volumes. Le premier, que vous avez en mains, se
concentre sur les concepts fondamentaux du langage Java, ainsi que sur les bases de la programmation dune interface graphique. Le second volume traite plus exhaustivement des fonctionnalits
dentreprise et de la programmation avance des interfaces utilisateur. Il aborde les sujets suivants :
m

les multithreads ;

la programmation rseaux ;

les objets distribus ;

les classes de collections ;

les bases de donnes ;

les concepts graphiques avancs ;

les composants GUI avancs ;

linternationalisation ;

les mthodes natives ;

JavaBeans ;

le traitement XML.

Lors de la rdaction dun ouvrage comme celui-ci, il est invitable de commettre des erreurs et des
inexactitudes. Nous avons donc prpar sur le site Web http://www.horstmann.com/corejava.html
une liste de questions courantes, de corrections et dexplications. Plac stratgiquement la fin de
page des corrections (pour vous encourager la lire), vous trouverez un formulaire permettant de
signaler des bogues et de suggrer des amliorations. Ne soyez pas dus si nous ne rpondons pas
chaque requte ou si nous ne vous crivons pas rapidement. Nous lisons tous les e-mails et apprcions
vos commentaires, qui nous permettent damliorer les futures versions de cet ouvrage.
Nous esprons que vous prendrez plaisir lire ce livre et quil vous aidera dans votre programmation.

Livre Java .book Page 3 Jeudi, 25. novembre 2004 3:04 15

Introduction

A propos de ce livre
Le Chapitre 1 prsentera les caractristiques de Java qui le distinguent des autres langages de
programmation. Nous expliquerons les intentions des concepteurs du langage et nous montrerons
dans quelle mesure ils sont parvenus leurs fins. Nous terminerons par un historique de Java et nous
prciserons la manire dont il a volu.
Le Chapitre 2 vous indiquera comment tlcharger et installer le JDK et les exemples de programme
du livre. Nous vous guiderons ensuite dans la compilation et lexcution de trois programmes Java
typiques : une application console, une application graphique et un applet, grce du JDK brut, un
diteur de texte activ pour Java et un IDE Java.
Nous entamerons dans le Chapitre 3 une tude approfondie du langage, en commenant par les
lments de base : les variables, les boucles et les fonctions simples. Si vous tes un programmeur C
ou C++, tout cela ne vous posera pas de problme, car la syntaxe employe est comparable celle de
C. Si vous avez une autre formation, par exemple en Visual Basic, nous vous conseillons de lire
attentivement ce chapitre.
Le programmation oriente objet (POO) est maintenant au cur des mthodes modernes de
programmation, et Java est un langage orient objet. Le Chapitre 4 prsentera lencapsulation la
premire des deux notions fondamentales de lorientation objet et le mcanisme du langage Java
qui permet de limplmenter, cest--dire les classes et les mthodes. En plus des rgles de Java, nous
vous proposerons des conseils pour une bonne conception oriente objet. Nous aborderons ensuite le
merveilleux outil javadoc qui permet de transformer les commentaires de votre code en une
documentation au format HTML. Si vous tes un habitu du C++, vous pourrez vous contenter de
parcourir rapidement ce chapitre. Les programmeurs qui ne sont pas familiariss avec la programmation oriente objet doivent se donner le temps dtudier ces concepts avant de poursuivre leur
exploration de Java.
Les classes et lencapsulation ne constituent quune partie du concept de POO, et le Chapitre 5 introduira lautre lment essentiel : lhritage. Celui-ci permet de rcuprer une classe existante et de la
modifier selon vos besoins. Il sagit l dune technique fondamentale de la programmation Java. Le
mcanisme dhritage de Java est comparable celui de C++. Ici encore, les programmeurs C++
pourront se concentrer uniquement sur les diffrences entre les deux langages.
Le Chapitre 6 vous montrera comment utiliser la notion dinterface. Les interfaces permettent de
dpasser le modle dhritage simple vu au Chapitre 5. En matrisant les interfaces, vous pourrez
profiter pleinement de lapproche oriente objet de la programmation Java. Nous traiterons
galement dans ce chapitre une caractristique technique trs utile de Java, appele classe interne.
Les classes internes permettent dobtenir des programmes plus propres et plus concis.
Dans le Chapitre 7, nous commencerons vritablement la programmation dapplications. Nous vous
montrerons comment crer des fentres, y dessiner et y tracer des figures gomtriques, formater du
texte avec diffrentes fontes et afficher des images.
Le Chapitre 8 sera consacr une tude dtaille du modle dvnement AWT. Nous verrons
comment crire le code permettant de rpondre des vnements tels que des clics de la souris ou
des frappes de touches. Vous verrez par la mme occasion comment grer des lments de linterface
utilisateur graphique comme les boutons ou les panneaux.

Livre Java .book Page 4 Jeudi, 25. novembre 2004 3:04 15

Au cur de Java 2 - Notions fondamentales

Le Chapitre 9 examinera de manire approfondie loutil Swing, qui permet de crer des interfaces
graphiques multi-plates-formes. Vous apprendrez tout ce quil faut savoir sur les diffrents types de
boutons, les composants de saisie, les bordures, les barres de dfilement, les zones de listes, les
menus et les botes de dialogue. Certains de ces composants, plus avancs, seront tudis au
Volume 2.
Lorsque vous en aurez termin avec le Chapitre 9, vous connatrez tous les mcanismes permettant
dcrire des applets, ces mini-programmes qui peuvent sexcuter dans une page Web. Nous leur
consacrerons le Chapitre 10. Nous vous proposerons un certain nombre dapplets utiles et amusants,
mais nous chercherons surtout vous les prsenter comme une mthode de dploiement de programmes. Nous vous indiquerons comment packager des applications dans des fichiers JAR et dlivrer
des applications sur Internet avec le mcanisme Java Web Start. Enfin, nous vous expliquerons
comment les programmes Java peuvent stocker et rcuprer des informations de configuration
lorsquelles ont t dployes.
Le Chapitre 11 traitera de la gestion des exceptions, un mcanisme robuste qui sappuie sur le fait
que mme les bons programmes peuvent subir des avanies. Par exemple, une connexion rseau peut
devenir indisponible au beau milieu dun tlchargement, ou un disque peut tre satur, etc. Les
exceptions fournissent un moyen efficace de sparer le code normal de traitement et la gestion
derreurs. Bien entendu, mme si vous avez scuris votre programme en grant toutes les conditions exceptionnelles, il nest pas certain quil fonctionne parfaitement. La deuxime partie de ce
chapitre vous donnera quelques astuces de dbogage. Pour terminer, nous vous accompagnerons
dans une session de dbogage avec diffrents outils : le dbogueur JDB, le dbogueur dun environnement de dveloppement intgr, un analyseur de performance (profiler), un outil de test de couverture du code et le robot AWT.
Le Chapitre 12 traitera de la gestion des entres/sorties. En Java, toutes les E/S sont gres par ce
quon appelle des flux. Ceux-ci vous permettent de travailler de manire uniforme avec toutes les
sources de donnes, quil sagisse des fichiers, des connexions rseau ou des blocs de mmoire.
Nous tudierons en dtail les classes de lecture et dcriture, qui facilitent le traitement dUnicode ;
nous vous montrerons galement ce qui se passe sous le capot lorsque vous employez le mcanisme
de srialisation, qui facilite et acclre la sauvegarde et le chargement des objets. Enfin, nous aborderons plusieurs bibliothques qui ont t ajoutes au JDK 1.4 : les classes "new I/O" (nouvelles E/S)
qui assurent la prise en charge des oprations de fichier avances et plus efficaces, et la bibliothque
dexpressions ordinaires.
Nous terminerons cet ouvrage par une vue densemble de la programmation gnrique, une avance
majeure du JDK 5.0. Elle facilite la lecture de vos programmes, tout en les scurisant. Nous vous
montrerons comment utiliser des types forts avec les collections et comment supprimer des transtypages peu srs.
LAnnexe A est consacre aux mots cls du langage Java.
LAnnexe B vous montre comment modifier les exemples de code, de sorte quils se compilent sur
une version plus ancienne du compilateur (JDK 1.4).

Livre Java .book Page 5 Jeudi, 25. novembre 2004 3:04 15

Introduction

Conventions
Comme cest lusage dans la plupart des ouvrages dinformatique, nous employons la police courrier
pour le code des programmes.
INFO C++
De nombreuses notes dinfo C++ vous prcisent les diffrences entre Java et C++. Vous pouvez ignorer ces notes si
vous ntes pas familiaris avec ce langage.

INFO
Ces informations sont signales par une icne de bloc-notes qui ressemble ceci.

ASTUCE
Les infos et les astuces sont repres par une de ces deux icnes.

ATTENTION
Une icne "Attention" vous prvient sil y a du danger lhorizon.

API Java

Java est accompagn dune importante bibliothque de programmation (API, Application


Programming Interface).
Lorsque nous utilisons pour la premire fois un appel lAPI, nous proposons galement une
brve description dans une note "API" situe la fin de la section. Ces descriptions sont un peu
plus informelles que celles de la documentation officielle en ligne, mais nous esprons quelles
sont plus instructives.
Les programmes dont le code source se trouve sur le Web sont fournis sous forme dexemples,
comme ceci :
Exemple 2.4 : WelcomeApplet.java
... Le code ici.

Exemples de code
Le site Web de cet ouvrage http://www.phptr.com/corejava contient tous les exemples du livre,
sous forme compresse. Ils peuvent tre dcompresss avec un des outils courants du march, ou
avec lutilitaire jar du kit JDK.
Reportez-vous au Chapitre 2 pour en savoir plus sur linstallation du JDK et des codes dexemple.

Livre Java .book Page 6 Jeudi, 25. novembre 2004 3:04 15

Livre Java .book Page 7 Jeudi, 25. novembre 2004 3:04 15

1
Une introduction Java
Au sommaire de ce chapitre

Java, plate-forme de programmation


Les termes cls du livre blanc de Java
Java et Internet
Bref historique de Java
Les ides fausses les plus rpandues concernant Java
La premire version de Java, sortie en 1996, a fait natre beaucoup de passions, pas seulement au
niveau de la presse informatique, mais galement dans la presse plus gnraliste comme The New
York Times, The Washington Post et Business Week. Java prsente lavantage dtre le premier et le
seul langage de programmation dont lhistoire est conte la radio. Il est plutt amusant de revisiter
cette poque pionnire, nous vous prsenterons donc un bref historique de Java dans ce chapitre.

Java, plate-forme de programmation


Dans la premire dition de cet ouvrage, nous avons d crire ceci :
"La rputation de Java en tant que langage informatique est exagre : Java est assurment un bon
langage de programmation. Il sagit, sans aucun doute, de lun des meilleurs disponibles pour un
programmeur srieux. Java aurait, potentiellement, pu tre un grand langage de programmation,
mais il est probablement trop tard pour cela. Lorsquun langage commence tre exploit, se pose le
problme de la compatibilit avec le code existant."
Un haut responsable de Sun, que nous ne nommerons pas, a adress notre diteur de nombreux
commentaires sur ce paragraphe. Mais, avec le temps, notre pronostic semble savrer. Java prsente
de trs bonnes fonctionnalits (nous les verrons en dtail plus loin dans ce chapitre). Il a pourtant sa
part dinconvnients, et les derniers ajouts ne sont pas aussi agrables que les premiers, et ce pour
des raisons de compatibilit.
Toutefois, comme nous le disions dans la premire dition, Java na jamais t quun langage. Mme
sil en existe foison, peu font beaucoup dclats. Java est une plate-forme complte, disposant

Livre Java .book Page 8 Jeudi, 25. novembre 2004 3:04 15

Au cur de Java 2 - Notions fondamentales

dune importante bibliothque, dune grande quantit de code rutilisable et dun environnement
dexcution qui propose des services tels que la scurit, la portabilit sur les systmes dexploitation
et le ramasse-miettes automatique.
En tant que programmeur, vous voulez un langage la syntaxe agrable et la smantique comprhensible (donc, pas de C++). Java rpond ces critres, comme des dizaines dautres langages.
Certains vous proposent la portabilit, le ramasse-miettes et des outils du mme genre, mais ils ne
disposent pas de vraie bibliothque, ce qui vous oblige dployer la vtre si vous souhaitez utiliser
de beaux graphiques, le rseau ou laccs aux bases de donnes. Java regroupe tout cela : un langage de
qualit, un environnement dexcution idoine et une grande bibliothque. Cest cette combinaison
qui fait de Java une proposition laquelle de nombreux programmeurs ne rsistent pas.

Les termes cls du livre blanc de Java


Les auteurs de Java ont crit un important livre blanc qui prsente les objectifs et les ralisations de
leur conception. Ce livre sarticule autour des onze termes cls suivants :
Simplicit

Portabilit

Orient objet

Interprt

Distribu

Performances leves

Fiabilit

Multithread

Scurit

Dynamique

Architecture neutre

Dans la suite de ce chapitre, nous allons :


m

rsumer par lintermdiaire dextraits du livre blanc ce que les concepteurs de Java ont voulu
traduire avec chacun de ces termes cls ;

exprimer ce que nous pensons de chaque terme, partir de notre exprience de la version
actuelle de Java.
INFO

A lheure o nous crivons ces lignes, le livre blanc est disponible ladresse suivante : http://java.sun.com/docs/
white/langenv/. Le rsum des onze mots cls figure ladresse ftp://ftp.javasoft.com/docs/papers/java-overview.ps.

Simplicit
*

Nous avons voulu crer un systme qui puisse tre programm simplement, sans ncessiter un apprentissage sotrique, et qui tire parti de lexprience standard actuelle. En consquence, mme si nous
pensions que C++ ne convenait pas, Java a t conu de faon relativement proche de ce langage dans
le dessein de faciliter la comprhension du systme. De nombreuses fonctions compliques, mal comprises,
rarement utilises de C++, qui nous semblaient par exprience apporter plus dinconvnients que
davantages, ont t supprimes de Java.

Livre Java .book Page 9 Jeudi, 25. novembre 2004 3:04 15

Chapitre 1

Une introduction Java

La syntaxe de Java reprsente rellement une version amliore de C++. Les fichiers den-tte,
larithmtique des pointeurs (ou mme une syntaxe de pointeur), les structures, les unions, la
surcharge doprateur, les classes de base virtuelles, etc. ne sont plus ncessaires (tout au long de cet
ouvrage, nous avons inclus des notes Info dcrivant plus prcisment les diffrences entre Java et
C++). Les concepteurs nont cependant pas tent de modifier certaines fonctions piges de C++
telles que linstruction switch. Si vous connaissez C++, le passage la syntaxe de Java vous
semblera facile.
Si vous tes habitu un environnement de programmation visuel (tel que Visual Basic), le langage
Java vous paratra plus complexe. Une partie de la syntaxe vous semblera trange (mme si sa
matrise est rapide). Plus important encore, la programmation en Java ncessitera davantage de
travail. Lintrt de Visual Basic rside dans le fait que son environnement visuel de conception fournit de faon presque automatique une grande partie de linfrastructure dune application. En Java, la
fonctionnalit quivalente doit tre programme laide des Java Swing, gnralement laide
dune quantit respectable de code. Il existe cependant des environnements de dveloppement tiers
qui permettent lcriture de programmes laide doprations "glisser-dplacer".
*

Un autre avantage de sa simplicit est sa petite taille. Lun des buts de Java est de permettre des logiciels de sexcuter intgralement sur de modestes machines. Ainsi, la taille cumule de linterprteur de
base et du support des classes est denviron 40 Ko ; pour supporter les classes standard ainsi que la
gestion multitraitement (threads), il faut ajouter 175 Ko.

Cest un exploit. Toutefois, sachez que la taille des bibliothques de linterface utilisateur graphique
(GUI) est notablement plus importante.

Orient objet
*

Pour rester simples, disons que la conception oriente objet est une technique de programmation qui se
concentre sur les donnes (les objets) et sur les interfaces avec ces objets. Pour faire une analogie avec
la menuiserie, on pourrait dire quun menuisier "orient objet" sintresse essentiellement la chaise
(lobjet) quil fabrique et non sa conception (le "comment"). Par opposition, le menuisier "non orient
objet" penserait dabord au "comment"...

Au cours des trente dernires annes, la programmation oriente objet a prouv ses avantages, et il
est inconcevable quun langage de programmation moderne nen tire pas parti. En fait, les fonctionnalits orientes objet de Java sont comparables celles de C++. Les diffrences majeures rsident
dans lhritage multiple (que Java a remplac par le concept plus simple des interfaces) et le modle
objet de Java (mtaclasses). Le mcanisme de rflexion (voir Chapitre 5) et la fonctionnalit de
srialisation des objets (voir Chapitre 12) facilitent normment la mise en uvre des objets persistants
et des gnrateurs GUI capables dintgrer des composants rutilisables.
INFO
Si vous navez aucune exprience des langages de programmation oriente objet, prenez soin de lire attentivement
les Chapitres 4 6. Ils vous expliquent ce quest la programmation oriente objet et pour quelles raisons elle est plus
utile la programmation de projets sophistiqus que ne le sont les langages procduraux comme C ou Basic.

Livre Java .book Page 10 Jeudi, 25. novembre 2004 3:04 15

10

Au cur de Java 2 - Notions fondamentales

Distribu
*

Java possde une importante bibliothque de routines permettant de grer les protocoles TCP/IP tels
que HTTP et FTP. Les applications Java peuvent charger, et accder , des objets sur Internet via des
URL avec la mme facilit quelles accdent un fichier local sur le systme.

Nous avons trouv que les fonctionnalits rseau de Java taient la fois fiables et dutilisation aise.
Toute personne ayant essay de faire de la programmation pour Internet avec un autre langage se
rjouira de la simplicit de Java lorsquil sagit de mettre en uvre des tches lourdes, comme
louverture dune connexion avec une socket (nous verrons les rseaux dans le Volume 2 de cet
ouvrage). Le mcanisme dinvocation de mthode distance (RMI) autorise la communication entre
objets distribus (voir galement le Volume 2).
Il existe maintenant une architecture spare, le Java 2 Entreprise Edition (J2EE), qui prend en
charge des applications distribues trs large chelle.

Fiabilit
*

Java a t conu pour que les programmes qui lutilisent soient fiables sous diffrents aspects. Sa
conception encourage le programmeur traquer prventivement les ventuels problmes, lancer des
vrifications dynamiques en cours dexcution et liminer les situations gnratrices derreurs... La
seule et unique grosse diffrence entre C++ et Java rside dans le fait que ce dernier intgre un modle
de pointeur qui carte les risques dcrasement de la mmoire et dendommagement des donnes.

Voil encore une caractristique fort utile. Le compilateur Java dtecte de nombreux problmes qui,
dans dautres langages, ne sont visibles quau moment de lexcution. Tous les programmeurs qui
ont pass des heures rcuprer une mmoire corrompue par un bogue de pointeur seront trs
heureux dexploiter cette caractristique de Java.
Si vous connaissez dj des langages comme Visual Basic, qui nexploite pas les pointeurs de faon
explicite, vous vous demandez srement pourquoi ce problme est si important. Les programmeurs
en C nont pas cette chance. Ils ont besoin des pointeurs pour accder des chanes, des tableaux,
des objets, et mme des fichiers. Dans Visual Basic, vous nemployez de pointeur pour aucune de
ces entits, pas plus que vous navez besoin de vous soucier de leurs allocations en mmoire. En
revanche, certaines structures de donnes sont difficiles implmenter sans laide de pointeurs. Java
vous donne le meilleur des deux mondes. Vous navez pas besoin des pointeurs pour les constructions habituelles, comme les chanes et les tableaux. Vous disposez de la puissance des pointeurs (via
des rfrences) en cas de ncessit, par exemple, pour construire des listes chanes. Et cela en toute
scurit dans la mesure o vous ne pouvez jamais accder un mauvais pointeur ni faire des erreurs
dallocation de mmoire, pas plus quil nest ncessaire de vous protger des "fuites" de mmoire.

Scurit
*

Java a t conu pour tre exploit dans des environnements serveur et distribus. Dans ce but, la scurit
na pas t nglige. Java permet la construction de systmes inaltrables et sans virus.

Dans la premire dition, nous dclarions : "Il ne faut jamais dire fontaine je ne boirai plus de ton
eau." Et nous avions raison. Peu aprs la publication de la premire version du JDK, un groupe
dexperts de la scurit de luniversit de Princeton a localis les premiers bogues des caractristiques

Livre Java .book Page 11 Jeudi, 25. novembre 2004 3:04 15

Chapitre 1

Une introduction Java

11

de scurit de Java 1.0. Sun Microsystems a encourag la recherche sur la scurit de Java, en
rendant publiques les caractristiques et limplmentation de la machine virtuelle et des bibliothques
de scurit. Il a rpar rapidement tous les bogues connus. Quoi quil en soit, Java se charge de
rendre particulirement difficile le contournement des mcanismes de scurit. Jusqu prsent, les
bogues qui ont t localiss taient trs subtils et (relativement) peu nombreux.
Ds le dpart, Java a t conu pour rendre impossibles certains types dattaques et, parmi eux :
m

la surcharge de la pile dexcution, une attaque commune des vers et des virus ;

lendommagement de la mmoire situe lextrieur de son propre espace de traitement ;

la lecture ou lcriture des fichiers sans autorisation.

Avec le temps, un certain nombre de fonctionnalits relatives la scurit ont t ajoutes Java.
Depuis la version 1.1, Java intgre la notion de classe signe numriquement (voir Au cur de
Java 2 Volume 2, ditions CampusPress), qui vous permet de savoir qui la crite. Votre degr
de confiance envers son auteur va dterminer lampleur des privilges que vous allez accorder la
classe sur votre machine.
INFO
Le mcanisme concurrent de mise disposition de code, labor par Microsoft et fond sur sa technologie ActiveX,
emploie exclusivement les signatures numriques pour assurer la scurit. Bien videmment, cela nest pas suffisant.
Tous les utilisateurs des produits Microsoft peuvent confirmer que les programmes proposs par des concepteurs
bien connus rencontrent des dfaillances qui provoquent des dgts. Le modle de scurit de Java est nettement
plus puissant que celui dActiveX dans la mesure o il contrle lapplication en cours dexcution et lempche de
faire des ravages.

Architecture neutre
*

Le compilateur gnre un format de fichier objet dont larchitecture est neutre le code compil
est excutable sur de nombreux processeurs, partir du moment o le systme dexcution de Java est
prsent. Pour ce faire, le compilateur Java gnre des instructions en bytecode (ou pseudo-code) qui
nont de lien avec aucune architecture dordinateur particulire. Au contraire, ces instructions ont t
conues pour tre la fois faciles interprter, quelle que soit la machine, et faciles traduire la vole
en code machine natif.

Lide nest pas nouvelle. Il y a plus de vingt ans, la mise en uvre originale de Pascal par Niklaus
Wirth et le systme Pascal UCSD utilisaient tous deux la mme approche.
Bien entendu, linterprtation des bytecodes est forcment plus lente que lexcution des instructions machine pleine vitesse. On peut donc douter de la pertinence de cette ide. Toutefois, les
machines virtuelles ont le choix de traduire les squences de bytecode frquemment utilises en
code machine, une procdure appele compilation en "juste--temps" (just-in-time ou JIT). Cette
stratgie sest rvle tellement efficace que la plate-forme .NET de Microsoft va jusqu reposer sur
une machine virtuelle.
La machine virtuelle prsente dautres avantages. Elle accrot la scurit car elle est en mesure de vrifier le comportement des suites dinstructions. Certains programmes vont jusqu produire des bytecodes
la vole, en amliorant dynamiquement les capacits dun programme en cours dexcution.

Livre Java .book Page 12 Jeudi, 25. novembre 2004 3:04 15

12

Au cur de Java 2 - Notions fondamentales

Portabilit
*

A la diffrence de C et de C++, on ne trouve pas les aspects de dpendance de la mise en uvre dans la
spcification. Les tailles des types de donnes primaires sont spcifies, ainsi que le comportement arithmtique qui leur est applicable.

Par exemple, en Java, un int est toujours un entier en 32 bits. Dans C/C++, int peut reprsenter un
entier 16 bits, un entier 32 bits, ou toute autre taille dcide par le concepteur du compilateur. La
seule restriction est que le type int doit contenir au moins autant doctets quun short int et ne
peut pas en contenir plus quun long int. Le principe dune taille fixe pour les types numriques
limine les principaux soucis du portage dapplications. Les donnes binaires sont stockes et
transmises dans un format fixe, ce qui met fin la confusion sur lordre des octets. Les chanes sont
enregistres au format Unicode standard.
*

Les bibliothques intgres au systme dfinissent des interfaces portables. Par exemple, il existe une
classe Window abstraite, accompagne de ses mises en uvre pour UNIX, Windows et Macintosh.

Tous ceux qui ont essay savent que llaboration dun programme compatible avec Windows,
Macintosh et dix versions dUNIX reprsente un effort hroque. Java 1.0 a accompli cet effort en
proposant un kit doutils simples sachant "coller" aux principaux composants dinterface utilisateur
dun grand nombre de plates-formes. Malheureusement, le rsultat tait une bibliothque qui donnait,
avec beaucoup de travail, des programmes peine acceptables sur diffrents systmes. En outre, on
rencontrait souvent des bogues diffrents sur les diffrentes implmentations graphiques. Mais ce
ntait quun dbut. Il existe de nombreuses applications pour lesquelles la portabilit est plus importante que lefficacit de linterface utilisateur, et ce sont celles qui ont bnfici des premires
versions de Java. Aujourdhui, le kit doutils de linterface utilisateur a t entirement rcrit de
manire ne plus dpendre de linterface utilisateur de lhte. Le rsultat est nettement plus cohrent,
et nous pensons quil est beaucoup plus intressant que dans les prcdentes versions de Java.

Interprt
*

Linterprteur de Java peut excuter les bytecodes directement sur nimporte quelle machine sur
laquelle il a t port. Dans la mesure o la liaison est un processus plus incrmentiel et lger, le processus
de dveloppement peut se rvler plus rapide et exploratoire.

La liaison incrmentielle prsente des avantages, mais ils ont t largement exagrs. Quoi quil en
soit, nous avons trouv les outils de dveloppement relativement lents. Si vous tes habitu la
vitesse de lenvironnement classique de Microsoft Visual Basic C++, vous serez certainement du
par les performances des environnements de dveloppement Java (toutefois, la version actuelle de
Visual Studio nest pas aussi dynamique que les environnements classiques. Quel que soit le langage
de programmation que vous utilisez, demandez votre suprieur de vous procurer un ordinateur plus
rapide pour lancer les derniers environnements de dveloppement).

Performances leves
*

En rgle gnrale, les performances des bytecodes interprts sont tout fait suffisantes ; il existe toutefois des situations dans lesquelles des performances plus leves sont ncessaires. Les bytecodes

Livre Java .book Page 13 Jeudi, 25. novembre 2004 3:04 15

Chapitre 1

Une introduction Java

13

peuvent tre traduits la vole (en cours dexcution) en code machine pour lunit centrale destine
accueillir lapplication.

Si vous employez un interprteur pour excuter les bytecodes, "performances leves" nest pas
prcisment la formule approprie. Il existe toutefois sur de nombreuses plates-formes une autre
forme de compilation propose par les compilateurs JIT (just-in-time, juste--temps). Ceux-ci
compilent les bytecodes en code natif, placent les rsultats dans le cache, puis les appellent en cas de
besoin. Cette approche augmente considrablement la vitesse dexcution du code couramment
utilis, puisque linterprtation nest ralise quune seule fois. Les compilateurs JIT nen restent pas
moins lgrement plus lents que les vrais compilateurs de code natif ; ils acclrent toutefois de dix
vingt fois certains programmes et sont presque toujours beaucoup plus rapides quun interprteur.
Cette technologie subit des amliorations constantes et finira peut-tre par donner des rsultats sans
commune mesure avec les systmes classiques de compilation. Par exemple, un compilateur JIT est
capable didentifier du code souvent excut et doptimiser exclusivement la vitesse de celui-ci.

Multithread
*

Les avantages du multithread sont une meilleure interractivit et un meilleur comportement en temps rel.

Si vous avez dj essay de programmer le multithread dans un autre langage, vous allez tre agrablement surpris de la simplicit de cette tche dans Java. Avec lui, les threads sont galement capables de tirer parti des systmes multiprocesseurs. Le ct ngatif rside dans le fait que les
implmentations de threads sur les plates-formes principales sont trs diffrentes, et Java ne fait
aucun effort pour tre indpendant de la plate-forme cet gard. La seule partie de code identique
entre les diffrentes machines est celle de lappel du multithread. Java se dcharge de limplmentation du multithread sur le systme dexploitation sous-jacent ou sur une bibliothque de threads (la
notion de thread est tudie au Volume 2). Malgr tout, la simplicit du multithread est lune des
raisons principales du succs de Java pour le dveloppement ct serveur.

Java, langage dynamique


*

Sur plusieurs points, Java est un langage plus dynamique que C ou C++. Il a t conu pour sadapter
un environnement en volution constante. Les bibliothques peuvent ajouter librement de nouvelles
mthodes et variables sans pour autant affecter leurs clients. La recherche des informations de type
excution dans Java est simple.

Cette fonction est importante lorsque lon doit ajouter du code un programme en cours dexcution.
Le premier exemple est celui du code tlcharg depuis Internet pour sexcuter dans un navigateur.
Avec la version 1.0 de Java, la recherche dinformation de type excution tait relativement
complexe. A lheure actuelle, les versions courantes de Java procurent au programmeur un aperu
complet de la structure et du comportement de ses objets. Cela se rvle trs utile pour les systmes
ncessitant une analyse des objets au cours de lexcution, tels que les gnrateurs graphiques de
Java, les dbogueurs volus, les composants enfichables et les bases de donnes objet.
INFO
Microsoft a sorti un produit intitul J++ qui prsente un lien de parent avec Java. Comme Java, J++ est excut par
une machine virtuelle compatible avec la machine virtuelle Java pour lexcution des bytecodes Java ; or il existe des

Livre Java .book Page 14 Jeudi, 25. novembre 2004 3:04 15

14

Au cur de Java 2 - Notions fondamentales

diffrences considrables lors de linterfaage avec un code externe. La syntaxe du langage de base est presque identique celle de Java. Toutefois, Microsoft a ajout des constructions de langage dune utilit douteuse, sauf pour
linterfaage avec lAPI Windows. Outre le fait que Java et J++ partagent une syntaxe commune, leurs bibliothques
de base (chanes, utilitaires, rseau, multithread, arithmtique, etc.) sont, pour lessentiel, identiques. Toutefois, les
bibliothques de graphiques, les interfaces utilisateur et laccs aux objets distants sont totalement diffrents. Pour
lheure, Microsoft ne prend plus en charge J++ mais a introduit un nouveau langage, appel C#, qui prsente aussi
de nombreuses similarits avec Java mais utilise une autre machine virtuelle. Il existe mme un J# pour faire migrer
les applications J++ vers la machine virtuelle utilise par C#. Sachez que nous ne reviendrons pas sur J++, C# ou J#
dans cet ouvrage.

Java et Internet
Lide de base est simple : les utilisateurs tlchargent les bytecodes Java depuis Internet et les
excutent sur leurs propres machines. Les programmes Java sexcutant sur les pages Web sont
nomms applets. Pour utiliser un applet, vous devez disposer dun navigateur Web compatible Java,
qui excutera les bytecodes. Sun fournit sous licence le code source de Java et affirme quaucun
changement naffectera le langage et la structure de la bibliothque de base : vous pouvez donc tre
assur quun applet Java sexcutera avec tout navigateur compatible Java. Malheureusement, la
ralit est diffrente. Plusieurs versions dInternet Explorer et de Netscape excutent diffrentes
versions de Java, certaines particulirement obsoltes. Cette situation complique considrablement
le dveloppement dapplets tirant parti de la version la plus rcente de Java. Pour remdier ce
problme, Sun a dvelopp le Java Plug-in. Cet outil met la disposition de Netscape et dInternet
Explorer lenvironnement dexcution de Java le plus rcent (voir Chapitre 10).
Lorsque lutilisateur tlcharge un applet, il sagit presque de lintgration dune image dans une
page Web. Lapplet sinsre dans la page et le texte se rpartit dans son espace. Le fait est que
limage est vivante. Elle ragit aux commandes utilisateur, change dapparence et transfre les
donnes entre lordinateur qui prsente lapplet et lordinateur qui la sert.
La Figure 1.1 prsente un exemple intressant de page Web dynamique, un applet qui permet dafficher des molcules et ralise des calculs sophistiqus. A laide de la souris, vous pouvez faire pivoter
chaque molcule et zoomer dessus, pour mieux en comprendre la structure. Ce type de manipulation
directe est impossible avec des pages Web statiques, elle nest possible quavec les applets (vous
trouverez cet applet ladresse http://jmol.sourceforge.net).
On peut aussi employer des applets pour ajouter des boutons et des champs dentre une page Web.
Mais le tlchargement de ces applets via une connexion tlphonique est trs lent. De plus, il vous
est possible dobtenir pratiquement le mme rsultat avec le HTML dynamique, les formulaires
HTML et un langage de script tel que JavaScript. Les premiers applets ont bien sr t utiliss pour
lanimation : les globes en rotation, les personnages anims de bande dessine, etc. Mais les animations GIF sont en mesure dexcuter tout cela. Les applets ne sont donc ncessaires que pour les
interactions riches, et non pour la conception de pages gnrales.
Les problmes de compatibilit des navigateurs et linconvnient du tlchargement du code des
applets via des connexions rseau lentes expliquent lchec relatif des applets sur les pages Web
dInternet. La situation est compltement diffrente sur les intranets. Dans ce cas, il nexiste aucun
problme de bande passante. Le temps de tlchargement des applets ne constitue donc pas un
problme. Dans ce type denvironnement, il est galement possible de contrler la version de navigateur utilise ou demployer le Java Plug-in de faon cohrente. Des programmes distribus pour

Livre Java .book Page 15 Jeudi, 25. novembre 2004 3:04 15

Chapitre 1

Une introduction Java

15

chaque utilisation via le Web ne peuvent tre incorrectement installs et configurs par les employs.
De plus, aucun dplacement de ladministrateur systme nest ncessaire pour mettre le code jour
sur les machines client. De nombreuses entreprises ont transform des programmes tels que le
contrle dinventaire, la planification des congs, le remboursement des notes de frais, etc., en
applets qui utilisent le navigateur comme plate-forme de distribution.
Figure 1.1
Lapplet Jmol.

Au moment o nous crivons ces lignes, la tendance revient des programmes orients client vers la
programmation ct serveur. En particulier, les serveurs dapplications peuvent utiliser les capacits
de surveillance de la machine virtuelle de Java pour raliser la rpartition automatique de la charge, le
regroupement de connexions base de donnes, la synchronisation dobjets, un arrt et un redmarrage
scuriss et autres oprations ncessaires pour les applications serveur volutives, mais qui sont de
toute vidence difficiles implmenter correctement. Les programmeurs dapplication ont donc intrt acheter ces mcanismes sophistiqus, plutt que les construire. Ils permettent daccrotre la
productivit du programmeur, qui peut se consacrer aux tches pour lesquelles il est le plus comptent
la logique de traitement de ses programmes au lieu de jongler avec les performances du serveur.

Bref historique de Java


Cette section prsente un bref historique de lvolution de Java. Elle se rfre diverses sources
publies (et, plus important encore, un entretien avec les crateurs de Java paru dans le magazine
en ligne SunWorld de juillet 1995).
La naissance de Java date de 1991, lorsquun groupe dingnieurs de Sun, dirig par Patrick Naughton
et James Gosling, voulut concevoir un petit langage informatique adapt des appareils de consommation comme les botes de commutation de cble TV. Ces appareils ne disposant que de trs peu de
puissance ou de mmoire, le langage devait tre concis et gnrer un code trs strict. Des constructeurs

Livre Java .book Page 16 Jeudi, 25. novembre 2004 3:04 15

16

Au cur de Java 2 - Notions fondamentales

diffrents tant susceptibles de choisir des units centrales diffrentes, il tait galement important
que le langage ne soit pas li une seule architecture. Le nom de code du projet tait "Green".
Les exigences dun code concis et indpendant de la plate-forme conduisirent lquipe reprendre le
modle que certaines implmentations de Pascal avaient adopt au dbut de lavnement des PC.
Niklaus Wirth, linventeur de Pascal, avait prconis la conception dun langage portable gnrant
du code intermdiaire pour des machines hypothtiques (souvent nommes machines virtuelles
de l, la machine virtuelle Java ou JVM). Ce code pouvait alors tre utilis sur toute machine disposant de linterprteur appropri. Les ingnieurs du projet Green utilisaient galement une machine
virtuelle, ce qui rsolvait leur principal problme.
Les employs de Sun avaient toutefois une culture UNIX. Ils ont donc bas leur langage sur C++
plutt que sur Pascal. Au lieu de crer un langage fonctionnel, ils ont mis au point un langage orient
objet. Cependant, comme Gosling lannonce dans linterview, "depuis toujours, le langage est un
outil et non une fin". Gosling dcida de nommer son langage "Oak" (probablement parce quil
apprciait la vue de sa fentre de bureau, qui donnait sur un chne). Les employs de Sun se sont
aperus plus tard que ce nom avait dj t attribu un langage informatique. Ils lont donc transform en Java. Ce choix sest rvl heureux.
Le projet Green a donn naissance au produit nomm "*7", en 1992. Il sagissait dun contrle
distance extrmement intelligent (il avait la puissance dune SPARCstation dans une bote de
15 10 10 cm). Malheureusement, personne ne fut intress pour le produire chez Sun. Lquipe
de Green dut trouver dautres ouvertures pour commercialiser sa technologie. Le groupe soumit
alors un nouveau projet. Il proposa la conception dun botier de cble TV capable de grer de
nouveaux services cbls, tels que la vido la demande. Malgr cela le contrat na pu tre dcroch
(pour la petite histoire, la compagnie qui la obtenu tait dirige par le mme Jim Clark qui avait
dmarr Netscape une entreprise qui a beaucoup particip au succs de Java).
Le projet Green (sous le nouveau nom de "First Person Inc.") a pass lanne 1993 et la moiti de
1994 rechercher des acheteurs pour sa technologie peine perdue (Patrick Naughton, lun des
fondateurs du groupe, prtend avoir parcouru plus de 80 000 km en avion pour vendre sa technologie). First Person a t dissoute en 1994.
Pendant ce temps, le World Wide Web dInternet devenait de plus en plus important. Llment cl
du Web est le navigateur qui traduit la page hypertexte lcran. En 1994, la plupart des gens utilisaient Mosaic, un navigateur Web non commercialis et issu du Supercomputing Center de luniversit de lIllinois en 1993 (alors quil tait encore tudiant, Marc Andreessen avait particip la
cration de Mosaic pour 6,85 $ lheure. Par la suite, il a obtenu la notorit et la fortune en tant que
cofondateur et directeur de technologie de Netscape).
Lors dune interview pour le SunWorld, Gosling a dclar quau milieu de lanne 1994, les dveloppeurs de langage avaient ralis quils pouvaient crer un navigateur vraiment cool. Il sagissait
dune des rares choses dans le courant client/serveur qui ncessitait certaines des actions bizarres
quils avaient ralises : larchitecture neutre, le temps rel, la fiabilit, la scurit des questions
qui taient peu importantes dans le monde des stations de travail. Ils ont donc cr un navigateur.
En fait, le vrai navigateur a t cr par Patrick Naughton et Jonathan Payne. Il a ensuite volu pour
donner naissance au navigateur HotJava actuel. Ce dernier a t crit en Java pour montrer la puissance de ce langage. Mais les concepteurs avaient galement lesprit la puissance de ce qui est
actuellement nomm les applets. Ils ont donc donn la possibilit au navigateur dexcuter le code

Livre Java .book Page 17 Jeudi, 25. novembre 2004 3:04 15

Chapitre 1

Une introduction Java

17

au sein des pages Web. Cette "dmonstration de technologie" fut prsente au SunWorld le 23 mai
1995, et elle fut lorigine de lengouement pour Java, qui ne sest pas dmenti.
Sun a diffus la premire version de Java dbut 1996. On a rapidement ralis que cette version ne
pouvait tre utilise pour un dveloppement dapplications srieux. Elle permettait, bien sr, de
crer un applet de texte anim qui se dplaait de faon alatoire sur un fond. Mais il tait impossible
dimprimer... Cette fonctionnalit ntait pas prvue par la version 1.02. Son successeur, la
version 1.1, a combl les fosss les plus vidents, a grandement amlior la capacit de rflexion et
ajout un nouveau modle pour la programmation GUI. Elle demeurait pourtant assez limite.
Les grands projets de la confrence JavaOne de 1998 taient la future version de Java 1.2. Cette
dernire tait destine remplacer les premires botes outils graphiques et GUI damateur par des
versions sophistiques et modulaires se rapprochant beaucoup plus que leurs prdcesseurs de la
promesse du "Write Once, Run Anywhere" (un mme programme sexcute partout). Trois jours
aprs (!) sa sortie en dcembre 1998, le service marketing de Sun a transform le nom, qui est
devenu Java 2 Standard Edition Software Development Kit Version 1.2 !
Outre "lEdition Standard", deux autres ditions ont t introduites : "Micro Edition" pour les services intgrs comme les tlphones portables, et "Entreprise Edition" pour le traitement ct serveur.
Cet ouvrage se concentre sur lEdition Standard.
Les versions 1.3 et 1.4 constituent une amlioration incrmentielle par rapport la version Java 2
initiale, avec une bibliothque standard en pleine croissance, des performances accrues et, bien
entendu, un certain nombre de bogues corrigs. Pendant ce temps, la majeure partie de la passion
gnre par les applets Java et les applications ct client a diminu, mais Java est devenu la plateforme de choix pour les applications ct serveur.
La version 5.0 est la premire depuis la version 1.1 qui actualise le langage Java de manire significative (cette version tait numrote, lorigine, 1.5, mais le numro est devenu 5.0 lors de la confrence JavaOne de 2004). Aprs de nombreuses annes de recherche, des types gnriques ont t
ajouts ( peu prs comparables aux modles C++), le dfi tant dintgrer cette fonctionnalit sans
exiger de changements de la machine virtuelle. Plusieurs autres fonctionnalits utiles ont t inspires par le C# : une boucle for each, lautoboxing (passage automatique entre type de base et classes
encapsulantes) et les mtadonnes. Les changements de langage continuent poser des problmes
de compatibilit, mais plusieurs de ces fonctionnalits sont si sduisantes que les programmeurs
devraient les adopter rapidement. Le Tableau 1.1 montre lvolution du langage Java.
Tableau 1.1 : Evolution du langage Java

Version

Nouvelles fonctionnalits du langage

1.0

Le langage lui-mme

1.1

Classes internes

1.2

Aucune

1.3

Aucune

1.4

Assertions

5.0

Classes gnriques, boucle for each, varargs, autoboxing, mtadonnes, numrations,


importation static

Livre Java .book Page 18 Jeudi, 25. novembre 2004 3:04 15

18

Au cur de Java 2 - Notions fondamentales

Le Tableau 1.2 montre lvolution de la bibliothque Java au cours des annes. Comme vous pouvez
le constater, la taille de linterface de programmation dapplication (API) a considrablement
augment.
Tableau 1.2 : Dveloppement de lAPI Java Standard Edition

Version

Nombre de classes et dinterfaces

1.0

211

1.1

477

1.2

1 524

1.3

1 840

1.4

2 723

5.0

3 270

Les ides fausses les plus rpandues concernant Java


Nous clturons ce chapitre par une liste de quelques ides fausses concernant Java. Elles seront
accompagnes de leur commentaire.
Java est une extension de HTML.
Java est un langage de programmation. HTML reprsente une faon de dcrire la structure dune
page Web. Ils nont rien en commun, lexception du fait quil existe des extensions HTML permettant
dinsrer des applets Java sur une page Web.
Jutilise XML, je nai donc pas besoin de Java.
Java est un langage de programmation ; XML est une manire de dcrire les donnes. Vous pouvez
traiter des donnes XML avec tout langage de programmation, mais lAPI Java en contient une
excellente prise en charge. En outre, de nombreux outils XML tiers trs importants sont mis en place
en Java. Voir le Volume 2 pour en savoir plus.
Java est un langage de programmation facile apprendre.
Aucun langage de programmation aussi puissant que Java nest facile. Vous devez toujours distinguer la facilit de lcriture de programmes triviaux et la difficult que reprsente un travail srieux.
Considrez galement que quatre chapitres seulement de ce livre traitent du langage Java. Les autres
chapitres dans les deux volumes traitent de la faon de mettre le langage en application, laide des
bibliothques Java. Celles-ci contiennent des milliers de classes et dinterfaces, et des dizaines de
milliers de fonctions. Vous navez heureusement pas besoin de connatre chacune dentre elles, mais
vous devez cependant tre capable den reconnatre un grand nombre pour pouvoir obtenir quelque
chose de raliste.

Livre Java .book Page 19 Jeudi, 25. novembre 2004 3:04 15

Chapitre 1

Une introduction Java

19

Java va devenir un langage de programmation universel pour toutes les plates-formes.


En thorie, cest possible, et il sagit certainement du souhait de tous les vendeurs, lexception de
Microsoft. Il existe cependant de nombreuses applications, dj parfaitement efficaces sur les ordinateurs de bureau, qui ne fonctionneraient pas sur dautres units ou lintrieur dun navigateur.
Ces applications ont t crites de faon tirer parti de la vitesse du processeur et de la bibliothque
de linterface utilisateur native. Elles ont t portes tant bien que mal sur toutes les plates-formes
importantes. Parmi ces types dapplications figurent les traitements de texte, les diteurs dimages et
les navigateurs Web. Ils sont crits en C ou C++, et nous ne voyons aucun intrt pour lutilisateur
final les rcrire en Java.
Java est simplement un autre langage de programmation.
Java est un bon langage de programmation. De nombreux programmeurs le prfrent C, C++ ou
C#. Mais des centaines de bons langages de programmation nont jamais russi percer, alors que
des langages avec des dfauts vidents, tels que C++ et Visual Basic, ont remport un large succs.
Pourquoi ? Le succs dun langage de programmation est bien plus dtermin par la qualit du
systme de support qui lentoure que par llgance de sa syntaxe. Existe-t-il des bibliothques
utiles, pratiques et standard pour les fonctions que vous envisagez de mettre en uvre ? Des
vendeurs doutils ont-ils cr de bons environnements de programmation et de dbogage ? Le
langage et lensemble des outils sintgrent-ils avec le reste de linfrastructure informatique ?
Java a du succs parce que ses bibliothques de classes vous permettent de raliser facilement ce
qui reprsentait jusqualors une tche complexe. La gestion de rseau et les multithreads en sont
des exemples. Le fait que Java rduise les erreurs de pointeur est un bon point. Il semble que les
programmeurs soient plus productifs ainsi. Mais il ne sagit pas de la source de son succs.
Larrive de C# rend Java obsolte.
C# a repris plusieurs bonnes ides de Java, par exemple un langage de programmation propre, une
machine virtuelle et un ramasse-miettes. Mais, quelles quen soient les raisons, le C# a galement
manqu certaines bonnes choses, comme la scurit et lindpendance de la plate-forme. Selon nous,
le plus gros avantage du C# reste son excellent environnement de dveloppement. Si vous apprciez
Windows, optez pour le C#. Mais, si lon en juge par les offres demploi, Java reste le langage
prfr de la majorit des dveloppeurs.
Java est un outil propritaire, il faut donc lviter.
Chacun agira selon sa conscience. Par moments, nous sommes dus par certains aspects de Java et
souhaitons quune quipe concurrente propose une solution source libre. Mais la situation nest pas
aussi simple.
Mme si Sun possde un contrle total sur Java, il a, par le biais de la "Communaut Java", impliqu
de nombreuses autres socits dans le dveloppement de versions et la conception de nouvelles
bibliothques. Le code source de la machine virtuelle et des bibliothques est disponible gratuitement,
mais pour tude uniquement et non pour modification et redistribution.
Si lon tudie les langages source libre qui existent, rien nindique quils fonctionnent mieux.
Les plus populaires sont les trois "P" dans "LAMP" (Linux, Apache, MySQL et Perl/PHP/Python).

Livre Java .book Page 20 Jeudi, 25. novembre 2004 3:04 15

20

Au cur de Java 2 - Notions fondamentales

Ces langages prsentent leurs avantages, mais ils ont aussi souffert de gros changements de versions,
de bibliothques limites et du manque doutils de dveloppement.
A lautre extrme, nous avons C++ et C#, qui ont t standardiss par des comits indpendants du
fournisseur. Certes, cette procdure est plus transparente que celle de la Communaut Java. Toutefois, les rsultats nont pas t aussi utiles quon aurait pu lesprer. Standardiser le langage et les
bibliothques les plus basiques ne suffit pas. Dans une vritable programmation, on dpasse rapidement la gestion des chanes, des collections et des fichiers. Dans le cas du C#, Microsoft a indiqu
quil mettrait la plupart des bibliothques hors de la procdure de standardisation.
Lavenir de Java rside peut-tre dans une procdure source libre. Mais, pour lheure, Sun a
convaincu de nombreuses personnes de sa qualit de leader responsable.
Java est interprt, il est donc trop lent pour les applications srieuses.
Aux premiers jours de Java, le langage tait interprt. Aujourdhui, sauf sur les plates-formes
"Micro" comme les tlphones portables, la machine virtuelle Java utilise un compilateur JIT. Les hot
spots de votre code sexcuteront aussi rapidement en Java quen C++.
Java est moins rapide que le C++, problme qui na rien voir avec la machine virtuelle. Le ramassemiettes est lgrement plus lent que la gestion manuelle de la mmoire, et les programmes Java,
fonctionnalits similaires, sont plus gourmands en mmoire que les programmes C++. Le dmarrage
du programme peut tre lent, en particulier avec de trs gros programmes. Les GUI Java sont plus
lents que leurs homologues natifs car ils sont conus indpendamment de la plate-forme.
Le public se plaint depuis des annes de la lenteur de Java par rapport au C++. Toutefois, les ordinateurs actuels sont plus rapides. Un programme Java lent sexcutera un peu mieux que ces programmes C++ incroyablement rapides dil y a quelques annes. Pour lheure, ces plaintes semblent
obsoltes et certains dtracteurs ont tendance viser plutt la laideur des interfaces utilisateur de
Java que leur lenteur.
Tous les programmes Java sexcutent dans une page Web.
Tous les applets Java sexcutent dans un navigateur Web. Cest la dfinition mme dun applet
un programme Java sexcutant dans un navigateur. Mais il est tout fait possible, et trs utile,
dcrire des programmes Java autonomes qui sexcutent indpendamment dun navigateur Web.
Ces programmes (gnralement nomms applications) sont totalement portables. Il suffit de prendre
le code et de lexcuter sur une autre machine ! Java tant plus pratique et moins sujet aux erreurs
que le langage C++ brut, il sagit dun bon choix pour lcriture des programmes. Ce choix sera
dautant meilleur quil sera associ des outils daccs aux bases de donnes tels que JDBC (Java
Database Connectivity) tudi au Chapitre 4 du Volume 2. Cest probablement le meilleur des choix
comme premier langage dapprentissage de la programmation.
La majeure partie des programmes de cet ouvrage sont autonomes. Les applets sont bien sr un sujet
passionnant. Mais les programmes Java autonomes sont plus importants et plus utiles dans la pratique.
Les programmes Java reprsentent un risque majeur pour la scurit.
Aux premiers temps de Java, son systme de scurit a quelquefois t pris en dfaut, et ces incidents ont t largement comments. La plupart sont dus limplmentation de Java dans un navigateur

Livre Java .book Page 21 Jeudi, 25. novembre 2004 3:04 15

Chapitre 1

Une introduction Java

21

spcifique. Les chercheurs ont considr ce systme de scurit comme un dfi relever et ont tent
de dtecter les failles de larmure de Java. Les pannes techniques dcouvertes ont toutes t rapidement corriges et, notre connaissance, aucun des systmes actuels na jamais t pris en dfaut.
Pour replacer ces incidents dans une juste perspective, prenez en compte les millions de virus qui
attaquent les fichiers excutables de Windows ainsi que les macros de Word. De rels dgts sont
alors causs, mais curieusement, peu de critiques sont mises concernant la faiblesse de la plateforme concerne. Le mcanisme ActiveX dInternet Explorer pourrait galement reprsenter une
cible facile prendre en dfaut, mais ce systme de scurit est tellement simple contourner que
trs peu ont pens publier leur dcouverte.
Certains administrateurs systme ont mme dsactiv Java dans les navigateurs de leur entreprise,
alors quils continuent autoriser les utilisateurs tlcharger des fichiers excutables, des contrles
ActiveX et des documents Word. Il sagit dun comportement tout fait ridicule actuellement, le
risque de se voir attaqu par un applet Java hostile est peu prs comparable au risque de mourir
dans un accident davion. Le risque dinfection inhrent louverture dun document Word est, au
contraire, comparable au risque de mourir en traversant pied une autoroute surcharge.
JavaScript est une version simplifie de Java.
JavaScript, un langage de script que lon peut utiliser dans les pages Web, a t invent par Netscape
et sappelait lorigine LiveScript. On trouve dans la syntaxe de JavaScript des rminiscences de
Java, mais il nexiste aucune relation ( lexception du nom, bien sr) entre ces deux langages. Un
sous-ensemble de JavaScript est standardis sous le nom de ECMA-262, mais les extensions requises pour pouvoir effectivement travailler nont pas t standardises. En consquence, lcriture dun
code JavaScript qui sexcute la fois dans Netscape et Internet Explorer est relativement difficile.
Avec Java, je peux remplacer mon ordinateur par une "bote noire Internet" bon march.
Lors de la premire sortie de Java, certains pariaient gros l-dessus. Depuis la premire dition de cet
ouvrage, nous pensons quil est absurde dimaginer que lon puisse abandonner une machine de
bureau puissante et pratique pour une machine limite sans mmoire locale. Cependant, un ordinateur rseau pourvu de Java est une option plausible pour une "initiative zro administration". En
effet, vous liminez ainsi le cot des ordinateurs de lentreprise, mais mme cela na pas tenu ses
promesses.
Par ailleurs, Java est devenu largement distribu sur les tlphones portables. Nous devons avouer
que nous navons pas encore vu dapplication Java indispensable fonctionnant sur les tlphones
portables, mais les jeux et les conomiseurs dcran usuels semblent bien se vendre sur de nombreux
marchs.
ASTUCE
Pour obtenir des rponses aux questions communes sur Java, consultez les FAQ Java sur le Web : http://
www.apl.jhu.edu/~hall/java/FAQs-and-Tutorials.html.

Livre Java .book Page 22 Jeudi, 25. novembre 2004 3:04 15

Livre Java .book Page 23 Jeudi, 25. novembre 2004 3:04 15

2
Lenvironnement
de programmation de Java
Au sommaire de ce chapitre

Installation du kit de dveloppement Java


Choix dun environnement de dveloppement
Utilisation des outils de ligne de commande
Utilisation dun environnement de dveloppement intgr
Compilation et excution des programmes partir dun diteur de texte
Excution dune application graphique
Elaboration et excution dapplets
Ce chapitre traite de linstallation du kit de dveloppement Java et de la faon de compiler et dexcuter
diffrents types de programmes : les programmes consoles, les applications graphiques et les applets.
Vous lancez les outils JDK en tapant les commandes dans une fentre shell. De nombreux programmeurs prfrent toutefois le confort dun environnement de dveloppement intgr. Vous apprendrez
utiliser un environnement disponible gratuitement, pour compiler et excuter les programmes Java.
Faciles comprendre et utiliser, les environnements de dveloppement intgrs sont longs charger
et ncessitent des ressources importantes. Comme solution intermdiaire, vous disposez des diteurs de
texte appelant le compilateur Java et excutant les programmes Java. Lorsque vous aurez matris les
techniques prsentes dans ce chapitre et choisi vos outils de dveloppement, vous serez prt aborder
le Chapitre 3, o vous commencerez explorer le langage de programmation Java.

Installation du kit de dveloppement Java


Les versions les plus compltes et les plus rcentes de Java 2 Standard Edition (J2SE) sont disponibles
auprs de Sun Microsystems pour Solaris, Linux et Windows. Certaines versions de Java sont disponibles en divers degrs de dveloppement pour Macintosh et de nombreuses autres plates-formes, mais
ces versions sont fournies sous licence et distribues par les fournisseurs de ces plates-formes.

Livre Java .book Page 24 Jeudi, 25. novembre 2004 3:04 15

24

Au cur de Java 2 - Notions fondamentales

Tlcharger le JDK
Pour tlcharger le JDK, accdez au site Web de Sun ; vous devrez passer une grande quantit de
jargon avant de pouvoir obtenir le logiciel.
Vous avez dj rencontr labrviation JDK (Java Development Kit). Pour compliquer un peu les
choses, les versions 1.2 1.4 du kit taient connues sous le nom de Java SDK (Software Development Kit). Sachez que vous retrouverez des mentions occasionnelles de cet ancien acronyme.
Vous rencontrerez aussi trs souvent le terme "J2SE". Il signifie "Java 2 Standard Edition", par
opposition J2EE (Java 2 Entreprise Edition) et J2ME (Java 2 Micro Edition).
Le terme "Java 2" est apparu en 1998, lanne o les commerciaux de Sun ont considr quaugmenter le numro de version par une dcimale ne traduisait pas correctement les nouveauts du JDK 1.2.
Or ils ne sen sont aperus quaprs la sortie et ont donc dcid de conserver le numro 1.2 pour le
kit de dveloppement. Les versions conscutives ont t numrotes 1.3, 1.4 et 5.0. La plate-forme a
toutefois t renomme de "Java" en "Java 2". Ce qui nous donne donc Java 2 Standard Edition
Development Kit version 5.0, soit J2SE 5.0.
Ceci peut tre assez dsarmant pour les ingnieurs, mais cest l le ct cach du marketing.
Pour les utilisateurs de Solaris, de Linux ou de Windows, accdez ladresse http://java.sun.com/
j2se pour tlcharger le JDK. Demandez la version 5.0 ou suprieure, puis choisissez votre plateforme.
Sun sort parfois des modules contenant la fois le Java Development Kit et un environnement de
dveloppement intgr. Cet environnement a, selon les poques, t nomm Forte, Sun ONE Studio,
Sun Java Studio et Netbeans. Nous ne savons pas ce que les arcanes du marketing auront trouv lorsque vous visiterez le site Web de Sun. Nous vous suggrons de ninstaller pour lheure que le Java
Development Kit. Si vous dcidez par la suite dutiliser lenvironnement de dveloppement intgr
de Sun, tlchargez-le simplement ladresse http://netbeans.org.
Une fois le JDK tlcharg, suivez les instructions dinstallation, qui sont fonction de la plate-forme.
A lheure o nous crivons, ils taient disponibles ladresse http://java.sun.com/j2se/5.0/
install.html.
Seules les instructions dinstallation et de compilation pour Java dpendent du systme. Une fois
Java install et oprationnel, les autres informations fournies dans ce livre sappliqueront votre
situation. Lindpendance vis--vis du systme est un avantage important de Java.
INFO
La procdure dinstallation propose un rpertoire dinstallation par dfaut incluant le numro de version de Java
JDK, comme jdk5.0. Ceci peut paratre pnible, mais le numro de version est finalement assez pratique, puisque
vous pouvez tester facilement une nouvelle version du JDK.
Sous Windows, nous vous recommandons fortement de ne pas accepter lemplacement par dfaut avec des espaces
dans le nom du chemin, comme C:\Program Files\jdk5.0. Enlevez simplement la partie Program Files.
Dans cet ouvrage, nous dsignons le rpertoire dinstallation par jdk. Par exemple, lorsque nous faisons rfrence au
rpertoire jdk/bin, nous dsignons le rpertoire ayant le nom /usr/local/jdk5.0/bin ou C:\jdk5.0\bin.

Livre Java .book Page 25 Jeudi, 25. novembre 2004 3:04 15

Chapitre 2

Lenvironnement de programmation de Java

25

Configurer le chemin dexcution


Aprs avoir install le JDK, vous devez effectuer une tape supplmentaire : ajouter le rpertoire
jdk/bin au chemin dexcution, la liste des rpertoires que traverse le systme dexploitation pour
localiser les fichiers excutables. Les directives concernant cette tape varient galement en fonction
du systme dexploitation.
m

Sous UNIX (y compris Solaris ou Linux), la procdure pour modifier le chemin dexcution
dpend du shell que vous utilisez. Si vous utilisez le C shell (qui est le dfaut pour Solaris), vous
devez ajouter une ligne analogue celle qui suit la fin de votre fichier ~/.cshrc:
set path=(/usr/local/jdk/bin $path)

Si vous utilisez le Bourne Again shell (dfaut pour Linux), ajoutez une ligne analogue celle qui
suit, la fin de votre fichier ~/.bashrc ou ~/.bash_profile:
export PATH=/usr/local/jdk/bin:$PATH

Pour les autres shells UNIX, vous devez rechercher les directives permettant de raliser la procdure analogue.
m

Sous Windows 95/98/Me, placez une ligne comme celle qui suit la fin de votre fichier C:\
AUTOEXEC.BAT:
SET PATH=c:\jdk\bin;%PATH%

Notez quil ny a pas despaces autour du signe =. Vous devez redmarrer votre ordinateur pour
rendre effective cette modification.
m

Sous Windows NT/2000/XP, ouvrez le Panneau de configuration, slectionnez Systme, puis


Environnement. Parcourez la fentre Variables utilisateur pour rechercher la variable nomme
PATH (si vous voulez mettre les outils Java disposition de tous les utilisateurs de votre machine,
utilisez plutt la fentre Variables systme). Ajoutez le rpertoire jdk\bin au dbut du chemin,
en ajoutant un point-virgule pour sparer la nouvelle entre, de la faon suivante :
c:\jdk\bin;le reste

Sauvegardez votre configuration. Toute nouvelle fentre console que vous lancerez comprendra
le chemin correct.
Procdez de la faon suivante pour vrifier que vous avez effectu les manipulations appropries :
1. Dmarrez une fentre shell. Cette opration dpend de votre systme dexploitation. Tapez la
ligne :
java -version

2. Appuyez sur la touche Entre. Vous devez obtenir un affichage comparable ce qui suit :
java version "5.0"
Java(TM) 2 Runtime Environment, Standard Edition
Java HotSpot(TM) Client VM

Si, la place, vous obtenez un message du type "java: command not found", "Bad command", "Bad
file name" ou "The name specified is not recognized as an internal or external command, operable
program or batch file", messages qui signalent une commande ou un fichier erron, vous devez vrifier
votre installation.

Livre Java .book Page 26 Jeudi, 25. novembre 2004 3:04 15

26

Au cur de Java 2 - Notions fondamentales

Installer la bibliothque et la documentation


Les fichiers source de bibliothque sont fournis dans le JDK sous la forme dun fichier compress
src.zip, et vous devez le dcompresser pour avoir accs au code source. La procdure suivante est
hautement recommande.
1. Assurez-vous que le JDK est install et que le rpertoire jdk/bin figure dans le chemin dexcution.
2. Ouvrez une commande shell.
3. Positionnez-vous dans le rpertoire jdk (par ex. /usr/local/jdk5.0 ou C:\jdk5.0).
4. Crez un sous-rpertoire src
mkdir src
cd src

5. Excutez la commande :
jar xvf ../src.zip

(ou jar xvf ..\src.zip sous Windows)


ASTUCE
Le fichier src.zip contient le code source pour toutes les bibliothques publiques. Vous pouvez obtenir dautres
codes source (pour le compilateur, la machine virtuelle, les mthodes natives et les classes prives helper), ladresse
http://www.sun.com/software/communitysource/j2se/java2/index.html.

La documentation est contenue dans un fichier compress spar du JDK. Vous pouvez tlcharger
la documentation ladresse http://java.sun.com/docs. Plusieurs formats (.zip, .gz et .Z) sont
disponibles. Dcompressez le format qui vous convient le mieux. En cas de doute, utilisez le fichier
zip, car vous pouvez le dcompresser laide du programme jar qui fait partie du JDK. Si vous
dcidez dutiliser jar, procdez de la faon suivante :
1. Assurez-vous que le JDK est install et que le rpertoire jdk/bin figure dans le chemin dexcution.
2. Copiez le fichier zip de documentation dans le rpertoire qui contient le rpertoire jdk. Le fichier
est nomm j2sdkversion-doc.zip, o version ressemble 5_0.
3. Lancez une commande shell.
4. Positionnez-vous dans le rpertoire jdk.
5. Excutez la commande :
jar xvf j2sdkversion-doc.zip

o version est le numro de version appropri.

Installer les exemples de programmes


Il est conseill dinstaller les exemples de programmes partir du CD-ROM ou de les tlcharger ladresse http://www.phptr.com/corejava. Les programmes sont compresss dans un
fichier zip corejava.zip. Dcompressez-les dans un rpertoire spar que nous vous recommandons dappeler CoreJavaBook. Vous pouvez utiliser nimporte quel utilitaire tel que

Livre Java .book Page 27 Jeudi, 25. novembre 2004 3:04 15

Chapitre 2

Lenvironnement de programmation de Java

27

WinZip (http:// www.winzip.com), ou simplement avoir recours lutilitaire jar qui fait partie
du JDK. Si vous utilisez jar, procdez de la faon suivante :
1. Assurez-vous que le JDK est install et que le rpertoire jdk/bin figure dans le chemin dexcution.
2. Crez un rpertoire nomm CoreJavaBook.
3. Copiez le fichier corejava.zip dans ce rpertoire.
4. Lancez une commande shell.
5. Positionnez-vous dans le rpertoire CoreJavaBook.
6. Excutez la commande :
jar xvf corejava.zip

Explorer les rpertoires de Java


Au cours de votre tude, vous aurez examiner des fichiers source Java. Vous devrez galement,
bien sr, exploiter au maximum la documentation bibliothque. Le Tableau 2.1 prsente larborescence des rpertoires du JDK.
Tableau 2.1 : Arborescence des rpertoires de Java

Structure du
rpertoire

Description
Le nom peut tre diffrent, par exemple jdk5.0

jdk
bin

Compilateur et outils

demo

Dmos

docs

Documentation de la bibliothque au format HTML (aprs dcompression


de j2sdkversion-doc.zip)

include

Fichiers pour les mthodes natives (voir Volume 2)

jre

Fichiers denvironnement dexcution de Java

lib

Fichiers de bibliothque

src

Source de bibliothque (aprs dcompression de src.zip)

Les deux sous-rpertoires les plus importants sont docs et src. Le rpertoire docs contient la
documentation de la bibliothque Java au format HTML. Vous pouvez la consulter laide de tout
navigateur Web tel que Netscape.
ASTUCE
Dfinissez un signet dans votre navigateur pour le fichier docs/api/index.html. Vous consulterez frquemment
cette page au cours de votre tude de la plate-forme Java !

Livre Java .book Page 28 Jeudi, 25. novembre 2004 3:04 15

28

Au cur de Java 2 - Notions fondamentales

Le rpertoire src contient le code source de la partie publique des bibliothques de Java. Lorsque ce
langage vous sera plus familier, ce livre et les informations en ligne ne vous apporteront sans doute
plus les infos dont vous avez besoin. Le code source de Java constituera alors un bon emplacement
o commencer les recherches. Il est quelquefois rassurant de savoir que lon a toujours la possibilit
de se plonger dans le code source pour dcouvrir ce qui est rellement ralis par une fonction de
bibliothque. Si vous vous intressez, par exemple, la classe System, vous pouvez consulter src/
java/lang/System.java.

Choix de lenvironnement de dveloppement


Si vous avez lhabitude de programmer avec Microsoft Visual Studio, vous tes accoutum un
environnement de dveloppement qui dispose dun diteur de texte intgr et de menus vous permettant de compiler et dexcuter un programme avec un dbogueur intgr. La version de base du JDK
ne contient rien de tel, mme approximativement. Tout se fait par lentre de commandes dans une
fentre shell. Nous vous indiquons comment installer et utiliser la configuration de base du JDK, car
nous avons constat que les environnements de dveloppement sophistiqus ne facilitent pas ncessairement lapprentissage de Java ils peuvent se rvler complexes et masquer certains dtails
intressants et importants pour le programmeur.
Les environnements de dveloppement intgrs sont plus fastidieux utiliser pour un programme
simple. Ils sont plus lents, ncessitent des ordinateurs plus puissants, et requirent une configuration
du projet assez lourde pour chaque programme que vous crivez. Ces environnements ont un lger
avantage si vous crivez des programmes Java plus importants qui contiennent de nombreux fichiers
source. Ces environnements fournissent aussi des dbogueurs et des systmes de contrle de la
version. Vous apprendrez dans cet ouvrage les rudiments de lutilisation dEclipse, un environnement de dveloppement disponible gratuitement et lui-mme crit en Java. Bien entendu, si vous
disposez dj dun environnement de dveloppement, tel que NetBeans ou JBuilder, qui prend en
charge la version actuelle de Java, nhsitez pas lutiliser.
Pour les programmes simples, un bon compromis entre les outils de ligne de commande et un
environnement de dveloppement intgr sera dutiliser un diteur qui sintgre au JDK. Sous
Linux, le meilleur choix est Emacs. Sous Windows, nous conseillons TextPad, un excellent diteur
de programmation shareware pour Windows sintgrant bien avec Java. Enfin, JEdit constitue une
excellente alternative multi-plate-forme. Un diteur de texte qui sintgre au JDK peut simplifier
et acclrer le dveloppement de programmes Java. Nous avons adopt cette approche pour le
dveloppement et le test de la plupart des programmes de cet ouvrage. Puisque vous pouvez
compiler et excuter le code source partir de lditeur, il peut devenir ici votre environnement de
dveloppement de facto.
En rsum, vous disposez de trois choix possibles pour un environnement de dveloppement :
m

Utiliser le JDK et votre diteur de texte prfr. Compilez et lancez les programmes dans une
commande shell.

Utiliser un environnement de dveloppement intgr tel quEclipse ou lun des autres environnements disponibles, gratuitement ou non.

Livre Java .book Page 29 Jeudi, 25. novembre 2004 3:04 15

Chapitre 2

Lenvironnement de programmation de Java

29

Utiliser le JDK et un diteur de texte intgrable au JDK. Emacs, TextPad et JEdit sont des choix
possibles, et il existe bien dautres programmes. Compilez et lancez les programmes au sein de
lditeur.

Utilisation des outils de ligne de commande


Commenons par le plus difficile : compiler et lancer un programme Java partir de la ligne de
commande.
Ouvrez un shell. Positionnez-vous dans le rpertoire CoreJavaBook/v1ch2/Welcome (le rpertoire
CoreJavaBook est celui dans lequel vous avez install le code source pour les exemples du livre, tel
quexpliqu prcdemment).
Entrez les commandes suivantes :
javac Welcome.java
java Welcome

Vous devez voir apparatre le message de la Figure 2.1 lcran.


INFO
Sous Windows, ouvrez une fentre shell comme suit : choisissez la commande Excuter du menu Dmarrer. Si vous
utilisez Windows NT/2000/XP, tapez cmd, sinon tapez command. Appuyez sur Entre, le shell apparat. Si vous navez
jamais utilis cette fonctionnalit, nous vous suggrons de suivre un didacticiel pour apprendre les bases des lignes
de commande. De nombreux dpartements denseignement ont install des didacticiels sur leurs sites tel (en anglais)
http://www.cs.sjsu.edu/faculty/horstman/CS46A/windows/tutorial.html.

Figure 2.1
Compilation et excution
de Welcome.java.

Livre Java .book Page 30 Jeudi, 25. novembre 2004 3:04 15

30

Au cur de Java 2 - Notions fondamentales

Flicitations ! Vous venez de compiler et dexcuter votre premier programme Java.


Que sest-il pass ? Le programme javac est le compilateur Java. Il compile le fichier Welcome.java en
un fichier Welcome.class. Le programme java lance la machine virtuelle Java. Il interprte les
bytecodes que le compilateur a placs dans le fichier class.
INFO
Si vous obtenez un message derreur relatif la ligne
for (String g: greeting)

cela signifie que vous utilisez probablement une ancienne version du compilateur Java. JDK 5.0 introduit plusieurs
fonctions utiles au langage de programmation Java dont nous profiterons dans cet ouvrage.
Si vous utilisez une ancienne version, vous devez rcrire la boucle comme suit :
for (int i = 0; i < greeting.length; i++=
System.out.println(greeting[i]);

Dans cet ouvrage, nous utilisons toujours les fonctionnalits du langage JDK 5.0. Leur transformation en leur quivalent de lancienne version est trs simple (voir lAnnexe B pour en savoir plus).

Le programme Welcome est extrmement simple. Il se contente dafficher un message sur lcran.
Vous pouvez examiner ce programme dans lExemple 2.1 nous en expliquerons le fonctionnement
dans le prochain chapitre.
Exemple 2.1 : Welcome.java
public class Welcome
{
public static void main(String[] args)
{
String[] greeting = new String[3];
greeting[0] = "Welcome to Core Java";
greeting[1] = "by Cay Horstmann";
greeting[2] = "and Gary Cornell";
for (String g: greeting)
System.out.println(g);
}
}

Conseils pour la recherche derreurs


A lheure des environnements de dveloppement visuels, les programmeurs nont pas lhabitude de
lancer des programmes dans une fentre shell. Tant de choses peuvent mal tourner et mener des
rsultats dcevants.
Surveillez particulirement les points suivants :
m

Si vous tapez le programme manuellement, faites attention aux lettres majuscules et minuscules.
En particulier, le nom de classe est Welcome et non welcome ou WELCOME.

Livre Java .book Page 31 Jeudi, 25. novembre 2004 3:04 15

Chapitre 2

Lenvironnement de programmation de Java

31

Le compilateur requiert un nom de fichier Welcome.java. Lorsque vous excutez le programme,


vous spcifiez un nom de classe (Welcome) sans extension .java ni .class.

Si vous obtenez un message tel que "Bad command or file name" ou "javac: command not
found", signalant une commande errone, vous devez vrifier votre installation, en particulier la
configuration du chemin dexcution.

Si javac signale une erreur "cannot read: Welcome.java", signalant une erreur de lecture du
fichier, vrifiez si ce fichier est prsent dans le rpertoire.
Sous UNIX, vrifiez que vous avez respect la casse des caractres pour Welcome.java.
Sous Windows, utilisez la commande shell dir, et non loutil Explorateur graphique. Certains
diteurs de texte (en particulier le Bloc-notes) ajoutent systmatiquement une extension .txt
aprs chaque fichier. Si vous utilisez le Bloc-notes pour modifier Welcome.java, le fichier sera
enregistr sous le nom Welcome.java.txt. Dans la configuration par dfaut de Windows,
lExplorateur conspire avec le Bloc-notes et masque lextension .txt, car elle est considre
comme un "type de fichier connu". Dans ce cas, vous devez renommer le fichier laide de la
commande shell ren ou le renregistrer, en plaant des guillemets autour du nom de fichier :
"Welcome.java".

Si java affiche un message signalant une erreur "java.lang.NoClassDefFoundError", vrifiez


soigneusement le nom de la classe concerne.
Si linterprteur se plaint que welcome contient un w minuscule, vous devez rmettre la
commande java Welcome avec un W majuscule. Comme toujours, la casse doit tre respecte
dans Java.
Si linterprteur signale un problme concernant Welcome/java, vous avez accidentellement
tap java Welcome.java. Rmettez la commande java Welcome.

Si vous avez tap java Welcome et que la machine virtuelle ne trouve pas la classe Welcome,
vrifiez si quelquun a configur le chemin de classe (class path) sur votre systme. Pour des
programmes simples, il vaut mieux lannuler. Pour ce faire, tapez set CLASSPATH=. Cette
commande fonctionne sous Windows et UNIX/Linux avec le shell C. Sous UNIX/Linux,
avec le shell Bourne/bash, utilisez export CLASSPATH=. Voir le Chapitre 4 pour plus de
dtails.

Si vous recevez un message derreur sur une nouvelle construction de langage, vrifiez que votre
compilateur supporte le JDK 5.0. Si vous ne parvenez pas utiliser le JDK 5.0 ou version ultrieure, modifiez le code source, comme indiqu lAnnexe B.

Si vous avez trop derreurs dans votre programme, tous les messages vont dfiler trs vite. Le
compilateur envoie les messages vers la sortie derreur standard, ce qui rend leur capture difficile
sils occupent plus dun cran.
Sur un systme UNIX ou Windows NT/2000/XP, vous pouvez utiliser loprateur shell 2> pour
rediriger les erreurs vers un fichier :
javac MyProg.java 2> errors.txt

Livre Java .book Page 32 Jeudi, 25. novembre 2004 3:04 15

32

Au cur de Java 2 - Notions fondamentales

Sous Windows 95/98/Me, il est impossible de rediriger le flux derreur standard partir du shell.
Vous pouvez tlcharger le programme errout partir de ladresse http://www.horstmann.com/corejava/faq.html et excuter
errout javac MyProg.java > errors.txt

ASTUCE
Il existe ladresse http://java.sun.com/docs/books/tutorial/getStarted/cupojava/ un excellent didacticiel qui
explore en dtail les piges qui peuvent drouter les dbutants.

Utilisation dun environnement de dveloppement intgr


Dans cette section, vous apprendrez compiler un programme laide dEclipse, un environnement
de dveloppement intgr gratuit disponible ladresse http://eclipse.org. Eclipse est crit en Java mais,
comme il utilise une bibliothque de fentre non standard, il nest pas aussi portable que Java. Il en
existe nanmoins des versions pour Linux, Mac OS X, Solaris et Windows.
Aprs le dmarrage dEclipse, choisissez File/New Project, puis slectionnez "Java Project" dans la
bote de dialogue de lassistant (voir Figure 2.2). Ces captures dcran proviennent dEclipse 3.0M8.
Votre version sera peut-tre lgrement diffrente.
Cliquez sur Next. Indiquez le nom du projet, savoir "Welcome", et tapez le nom de chemin complet
jusquau rpertoire qui contient Welcome.java ; consultez la Figure 2.3. Vrifiez que loption intitule
"Create project in workspace" est dcoche. Cliquez sur Finish.
Figure 2.2
Bote de dialogue
New Project dans Eclipse.

Le projet est maintenant cr. Cliquez sur le triangle situ dans le volet de gauche prs de la fentre
de projet pour louvrir, puis cliquez sur le triangle prs de "Default package". Double-cliquez sur
Welcome.java. Une fentre souvre qui contient le code du programme (voir Figure 2.4).

Livre Java .book Page 33 Jeudi, 25. novembre 2004 3:04 15

Chapitre 2

Lenvironnement de programmation de Java

33

Figure 2.3
Configuration
dun projet Eclipse.

Figure 2.4
Modification dun fichier
source avec Eclipse.

Cliquez sur le nom du projet (Welcome) du bouton droit de la souris, dans le volet le plus gauche.
Slectionnez Build Project dans le menu qui apparat. Votre programme est maintenant compil. Si
lopration russit, choisissez Run/Run As/Java Application. Une fentre apparat au bas de la fentre.
Le rsultat du programme sy affiche (voir Figure 2.5).

Livre Java .book Page 34 Jeudi, 25. novembre 2004 3:04 15

34

Au cur de Java 2 - Notions fondamentales

Figure 2.5
Excution
dun programme
dans Eclipse.

Localiser les erreurs de compilation


Notre programme ne devrait pas contenir derreur de frappe ou de bogue (aprs tout, il ne comprend que
quelques lignes de code). Supposons, pour la dmonstration, quil contienne une coquille (peut-tre
mme une erreur de syntaxe). Essayez dexcuter le fichier en modifiant la casse de String de la faon
suivante :
public static void main(string[] args)

Compilez nouveau le programme. Vous obtiendrez des messages derreur (voir Figure 2.6) qui
signalent un type string inconnu. Cliquez simplement sur le message. Le curseur se positionne sur
la ligne correspondante dans la fentre ddition pour vous permettre de corriger lerreur.
Figure 2.6
Des messages derreur
dans Eclipse.

Livre Java .book Page 35 Jeudi, 25. novembre 2004 3:04 15

Chapitre 2

Lenvironnement de programmation de Java

35

Ces instructions doivent vous amener vouloir travailler dans un environnement intgr. Nous
tudierons le dbogueur Eclipse au Chapitre 11.

Compilation et excution de programmes partir dun diteur


de texte
Un environnement de dveloppement intgr procure de nombreux avantages, mais prsente aussi
certains inconvnients. En particulier, sil sagit de programmes simples qui ne sont pas rpartis en
plusieurs fichiers source, un tel environnement avec un temps de dmarrage assez long et de
nombreuses fioritures peut sembler quelque peu excessif. Heureusement, de nombreux diteurs de texte
ont la possibilit de lancer le compilateur et des programmes Java et dintercepter les messages
derreur et la sortie du programme. Dans cette section, nous allons tudier un exemple typique
dditeur de texte, Emacs.
INFO
GNU Emacs est disponible ladresse http://www.gnu.org/software/emacs/. Pour le port Windows de GNU
Emacs, voyez http://www.gnu.org/software/emacs/windows/ntemacs.html. Veillez installer le package JDEE
(Java Development Environment for Emacs) si vous utilisez Emacs pour Java. Vous pouvez le tlcharger
ladresse http://jdee.sunsite.dk. Pour JDK 5.0, vous devez utiliser JDEE version 2.4.3beta 1 ou suprieure.

Emacs est un merveilleux diteur de texte, disponible gratuitement pour UNIX, Linux,
Windows et Mac OS X. Pourtant, de nombreux programmeurs Windows rechignent apprendre
lutiliser. Nous leur recommandons alors dutiliser TextPad. A la diffrence dEmacs, TextPad
se conforme aux conventions Windows. Il est disponible ladresse http://www.textpad.com.
Sachez quil sagit dun shareware. Vous tes cens payer pour lutiliser au-del de la priode
dessai (nous navons aucun intrt le faire vendre, mais nous sommes trs satisfaits de son
utilisation).
Autre choix populaire : JEdit, un trs bon diteur crit en Java et disponible gratuitement ladresse
http://jedit.org. Que vous utilisiez Emacs, TextPad, JEdit ou un autre diteur, lide est la mme.
Lditeur lance le compilateur et capture les messages derreur. Vous corrigez les erreurs, recompilez
le programme et appellez une autre commande pour excuter votre programme.
La Figure 2.7 montre lditeur Emacs compilant un programme Java (choisissez JDE/Compile dans
le menu pour lancer le compilateur).
Les messages derreur saffichent dans la moiti infrieure de lcran. Lorsque vous dplacez le
curseur sur un message derreur et que vous appuyez sur la touche Entre, le curseur se positionne
sur la ligne correspondante du code source.
Lorsque toutes les erreurs ont t corriges, vous pouvez excuter le programme en choisissant JDE/
Run App dans le menu. La sortie saffiche dans une fentre ddition (voir Figure 2.8).

Livre Java .book Page 36 Jeudi, 25. novembre 2004 3:04 15

36

Au cur de Java 2 - Notions fondamentales

Figure 2.7
Compilation
dun programme
avec Emacs.

Figure 2.8
Excution
dun programme
partir dEmacs.

Livre Java .book Page 37 Jeudi, 25. novembre 2004 3:04 15

Chapitre 2

Lenvironnement de programmation de Java

37

Excution dune application graphique


Le programme Welcome ne prsentait pas beaucoup dintrt. Excutons maintenant une application
graphique. Il sagit dun afficheur simple de fichiers image. Compilons ce programme et excutonsle depuis la ligne de commande.
1. Ouvrez une fentre shell.
2. Placez-vous sur le rpertoire CoreJavaBook/v1ch2/ImageViewer.
3. Tapez :
javac ImageViewer.java
java ImageViewer

Une nouvelle fentre de programme apparat avec notre visionneuse, ImageViewer (voir Figure 2.9).
Slectionnez maintenant File/Open et recherchez le fichier GIF ouvrir (nous avons inclus quelques
exemples de fichiers dans le mme rpertoire).
Pour fermer le programme, cliquez sur le bouton de fermeture dans la barre de titre ou droulez le
menu systme et choisissez Quitter (pour compiler et excuter ce programme dans un diteur de
texte ou un environnement de dveloppement intgr, procdez comme prcdemment. Par exemple,
dans le cas dEmacs, choisissez JDE/Compile, puis JDE/Run App.
Figure 2.9
Excution de lapplication
ImageViewer.

Nous esprons que vous trouverez ce programme pratique et intressant. Examinez rapidement le
code source. Ce programme est plus long que le prcdent, mais il nest pas trs compliqu en
comparaison avec la quantit de code qui aurait t ncessaire pour crire une application analogue
en C ou C++. Ce type de programme est bien sr trs facile crire avec Visual Basic ou plutt
copier et coller. Le JDK ne propose pas de gnrateur dinterface visuel, vous devez donc tout
programmer la main (voir Exemple 2.2). Vous apprendrez crer des programmes graphiques tels
que celui-ci aux Chapitres 7 9.

Livre Java .book Page 38 Jeudi, 25. novembre 2004 3:04 15

38

Au cur de Java 2 - Notions fondamentales

Exemple 2.2 : ImageViewer.java


import
import
import
import

java.awt.*;
java.awt.event.*;
java.io.*;
javax.swing.*;

/**
Un programme permettant dafficher des images.
*/
public class ImageViewer
{
public static void main(String[] args)
{
JFrame frame = new ImageViewerFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec une tiquette permettant dafficher une image.
*/
class ImageViewerFrame extends JFrame
{
public ImageViewerFrame()
{
setTitle("ImageViewer");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// utiliser une tiquette pour afficher les images
label = new JLabel();
add(label);
// configurer le slecteur de fichiers
chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
// configurer la barre de menus
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem openItem = new JMenuItem("Open");
menu.add(openItem);
openItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// montrer la bote de dialogue du slecteur
int result = chooser.showOpenDialog(null);
// en cas de slection dun fichier, dfinir comme icne
// de ltiquette
if (result == JFileChooser.APPROVE_OPTION)

Livre Java .book Page 39 Jeudi, 25. novembre 2004 3:04 15

Chapitre 2

Lenvironnement de programmation de Java

39

{
String name = chooser.getSelectedFile().getPath();
label.setIcon(new ImageIcon(name));
}
}
});
JMenuItem exitItem = new JMenuItem("Exit");
menu.add(exitItem);
exitItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
private
private
private
private

JLabel label;
JFileChooser chooser;
static final int DEFAULT_WIDTH = 300;
static final int DEFAULT_HEIGHT = 400;

Elaboration et excution dapplets


Les deux premiers programmes prsents dans cet ouvrage sont des applications Java, des programmes autonomes comme tout programme natif. Comme nous lavons mentionn dans le dernier
chapitre, la rputation de Java est due en grande partie sa capacit excuter des applets dans un
navigateur Web. Nous allons vous montrer comment crer et excuter un applet partir de la ligne de
commande. Puis nous allons charger lapplet dans lditeur dapplets fourni avec le JDK et enfin,
nous lafficherons dans un navigateur Web.
Ouvrez un shell et placez-vous dans le rpertoire CoreJavaBook/v1ch2/WelcomeApplet, puis
saisissez les commandes suivantes :
javac WelcomeApplet.java
appletviewer WelcomeApplet.html

La Figure 2.10 montre ce que vous voyez dans la fentre de lafficheur dapplets.
Figure 2.10
Lapplet WelcomeApplet
dans lafficheur dapplets.

Livre Java .book Page 40 Jeudi, 25. novembre 2004 3:04 15

40

Au cur de Java 2 - Notions fondamentales

La premire commande, qui nous est maintenant familire, est la commande dappel du compilateur
Java. Celui-ci compile le fichier source WelcomeApplet.java et produit le fichier de bytecodes
WelcomeApplet.class.
Cependant, nous nexcutons pas cette fois linterprteur Java, mais nous invoquons le programme
appletviewer. Ce programme est un outil particulier inclus avec le JDK qui vous permet de tester
rapidement un applet. Vous devez lui transmettre un fichier HTML plutt que le nom dun fichier de
classe java. Le contenu du fichier WelcomeApplet.html est prsent dans lExemple 2.3 ci-aprs.
Exemple 2.3 : WelcomeApplet.html
<html>
<head>
<title>WelcomeApplet</title>
</head>
<body>
<hr/>
<p>
This applet is from the book
<a href="http://www.horstmann.com/corejava.html">Core Java</a>
by <em>Cay Horstmann</em> and <em>Gary Cornell</em>,
published by Sun Microsystems Press.
</p>
<applet code=WelcomeApplet.class width="400" height="200">
<param name="greeting" value="Welcome to Core Java!"/>
</applet>
<hr/>
<p><a href="WelcomeApplet.java">The source.</a></p>
</body>
</html>

Si vous connaissez le code HTML, vous remarquerez quelques instructions standard et la balise
applet qui demande lafficheur de charger lapplet dont le code est stock dans WelcomeApplet.class. Cet afficheur dapplets ignore toutes les balises HTML excepte la balise applet.
Les autres balises sont visibles si vous affichez le fichier HTML dans un navigateur Java 2. Cependant,
la situation des navigateurs est un peu confuse.
m

Mozilla (et Netscape 6 et versions ultrieures) prennent en charge Java 2 sous Windows, Linux
et Mac OS X. Pour tester ces applets, tlchargez simplement la dernire version et vrifiez que
Java est activ.

Certaines versions dInternet Explorer ne prennent pas du tout en charge Java. Dautres ne prennent en charge que la trs obsolte machine virtuelle Microsoft Java. Si vous excutez Internet
Explorer sous Windows, accdez ladresse http://java.com et installez le plug-in Java.

Safari et Internet Explorer sont intgrs dans limplmentation Macintosh Java de Macintosh
sous OS X, qui prend en charge JDK 1.4 au moment o nous rdigeons. OS 9 ne supporte que la
vieille version 1.1.

A condition davoir un navigateur compatible Java 2, vous pouvez tenter de charger lapplet dans le
navigateur.
1. Dmarrez votre navigateur.
2. Slectionnez Fichier/Ouvrir (ou lquivalent).
3. Placez-vous sur le rpertoire CoreJavaBook/v1ch2/WelcomeApplet.

Livre Java .book Page 41 Jeudi, 25. novembre 2004 3:04 15

Chapitre 2

Lenvironnement de programmation de Java

41

Vous devez voir apparatre le fichier WelcomeApplet.html dans la bote de dialogue. Chargez le
fichier. Votre navigateur va maintenant charger lapplet avec le texte qui lentoure. Votre cran doit
ressembler celui de la Figure 2.11.
Vous pouvez constater que cette application est rellement dynamique et adapte lenvironnement
Internet. Cliquez sur le bouton Cay Horstmann, lapplet commande au navigateur dafficher la page
Web de Cay. Cliquez sur le bouton Gary Cornell, lapplet lance laffichage dune fentre de courrier
lectronique avec ladresse e-mail de Gary prenregistre.
Figure 2.11
Excution de lapplet
WelcomeApplet dans
un navigateur.

Vous remarquerez quaucun de ces deux boutons ne fonctionne dans lafficheur dapplets. Cet afficheur na pas la possibilit denvoyer du courrier ou dafficher une page Web, il ignore donc vos
demandes. Lafficheur dapplets est uniquement destin tester les applets de faon isole, mais
vous devrez placer ces dernires dans un navigateur pour contrler leur interaction avec le navigateur
et Internet.
ASTUCE
Vous pouvez aussi excuter des applets depuis votre diteur ou votre environnement de dveloppement intgr.
Dans Emacs, slectionnez JDE/Run Applet dans le menu. Dans Eclipse, utilisez Run/Run as/Java Applet.

Pour terminer, le code de lapplet est prsent dans lExemple 2.4. A ce niveau de votre tude, contentezvous de lexaminer rapidement. Nous reviendrons sur lcriture des applets au Chapitre 10.
Dans ce chapitre, vous avez appris les mcanismes de la compilation et de lexcution des programmes Java. Vous tes maintenant prt aborder le Chapitre 3, o vous attaquerez lapprentissage du
langage Java.

Livre Java .book Page 42 Jeudi, 25. novembre 2004 3:04 15

42

Au cur de Java 2 - Notions fondamentales

Exemple 2.4 : WelcomeApplet.java


import
import
import
import

javax.swing.*;
java.awt.*;
java.awt.event.*;
java.net.*;

public class WelcomeApplet extends JApplet


{
public void init()
{
setLayout(new BorderLayout());
JLabel label = new JLabel(getParameter("greeting"),
SwingConstants.CENTER);
label.setFont(new Font("Serif", Font.BOLD, 18));
add(label, BorderLayout.CENTER);
JPanel panel = new JPanel();
JButton cayButton = new JButton("Cay Horstmann");
cayButton.addActionListener(getURLActionListener
("http://www.horstmann.com"));
panel.add(cayButton);
JButton garyButton = new JButton("Gary Cornell");
garyButton.addActionListener(makeURLActionListener
("mailto:gary@thecornells.com"));
panel.add(garyButton);
add(panel, BorderLayout.SOUTH);
}
private ActionListener makeURLActionListener(final String u)
{
return new
ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
try
{
getAppletContext().showDocument(new URL(u));
}
catch(MalformedURLException e)
{
e.printStackTrace();
}
}
};
}
}

Livre Java .book Page 43 Jeudi, 25. novembre 2004 3:04 15

3
Structures fondamentales
de la programmation Java
Au sommaire de ce chapitre

Un exemple simple de programme Java


Commentaires
Types de donnes
Variables
Oprateurs
Chanes
Entres et sorties
Flux de contrle
Grands nombres
Tableaux
Nous supposons maintenant que vous avez correctement install le JDK et que vous avez pu excuter
les exemples de programmes proposs au Chapitre 2. Il est temps daborder la programmation.
Ce chapitre vous montrera comment sont implments en Java certains concepts fondamentaux de la
programmation, tels que les types de donnes, les instructions de branchement et les boucles.
Malheureusement, Java ne permet pas dcrire facilement un programme utilisant une interface
graphique il faut connatre de nombreuses fonctionnalits pour construire des fentres, ajouter
des zones de texte, des boutons et les autres composants dune interface. La prsentation des
techniques exiges par une interface graphique nous entranerait trop loin de notre sujet les
concepts fondamentaux de la programmation et les exemples de programmes proposs dans
ce chapitre ne seront que des programmes conus pour illustrer un concept. Tous ces exemples utilisent
simplement une fentre shell pour lentre et la sortie dinformations.

Livre Java .book Page 44 Jeudi, 25. novembre 2004 3:04 15

44

Au cur de Java 2 - Notions fondamentales

Si vous tre un programmeur C++ expriment, vous pouvez vous contenter de parcourir ce chapitre
et de vous concentrer uniquement sur les rubriques Info C++. Les programmeurs qui viennent dun
autre environnement, comme Visual Basic, rencontreront la fois des concepts familiers et une
syntaxe trs diffrente : nous leur recommandons de lire ce chapitre attentivement.

Un exemple simple de programme Java


Examinons le plus simple des programmes Java ; il se contente dafficher un message la console :
public class firstSample
{
public static void main(String[] args)
{
System.out.println("We will not use Hello, World!");
}
}

Mme si cela doit prendre un peu de temps, il est ncessaire de vous familiariser avec la prsentation de
cet exemple ; les lments qui le composent se retrouveront dans toutes les applications. Avant tout,
prcisons que Java est sensible la casse des caractres (majuscules et minuscules). Si vous commettez
la moindre erreur en tapant, par exemple, Main au lieu de main, le programme ne pourra pas sexcuter !
Etudions maintenant le code source, ligne par ligne. Le mot cl public est appel un modificateur
daccs (ou encore un spcificateur daccs ou spcificateur de visibilit) ; les modificateurs dterminent quelles autres parties du programme peuvent tre utilises par notre exemple. Nous reparlerons des modificateurs au Chapitre 5. Le mot cl class est l pour vous rappeler que tout ce que lon
programme en Java se trouve lintrieur dune classe. Nous tudierons en dtail les classes dans le
prochain chapitre, mais considrez ds prsent quune classe est une sorte de conteneur renfermant
la logique du programme qui dfinit le comportement dune application. Comme nous lavons vu au
Chapitre 1, les classes sont des briques logicielles avec lesquelles sont construites toutes les applications
ou applets Java. Dans un programme Java, tout doit toujours se trouver dans une classe.
Derrire le mot cl class se trouve le nom de la classe. Les rgles de Java sont assez souples en ce
qui concerne lattribution des noms de classes. Ceux-ci doivent commencer par une lettre et peuvent
ensuite contenir nimporte quelle combinaison de lettres et de chiffres. Leur taille nest pas limite.
Il ne faut cependant pas attribuer un mot rserv de Java (comme public ou class) un nom de
classe (vous trouverez une liste des mots rservs dans lAnnexe A).
Comme vous pouvez le constater avec notre classe FirstSample, la convention gnralement
admise est de former les noms de classes avec des substantifs commenant par une majuscule.
Lorsquun nom est constitu de plusieurs mots, placez linitiale de chaque mot en majuscule.
Il faut donner au fichier du code source le mme nom que celui de la classe publique, avec lextension .java. Le code sera donc sauvegard dans un fichier baptis FirstSample.java (rptons que
la casse des caractres est importante, il ne faut pas nommer le fichier firstsample.java).
Si vous navez pas commis derreur de frappe en nommant le fichier et en tapant le code source, la
compilation de ce code produira un fichier contenant le pseudo-code de la classe. Le compilateur Java
nomme automatiquement le fichier de pseudo-code FirstSample.class et le sauvegarde dans le mme
rpertoire que le fichier source. Lorsque tout cela est termin, lancez le programme laide de la
commande suivante : java FirstSample.

Livre Java .book Page 45 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

45

Ne spcifiez pas lextension .class. Lexcution du programme affiche simplement la chane We


will not use Hello, World! la console.
Lorsque vous tapez la commande
java NomDeClasse

pour lancer un programme compil, la machine virtuelle dmarre toujours lexcution par les
instructions de la mthode main de la classe spcifie. Par consquent, vous devez crire une
mthode main dans le fichier source de la classe pour que le code puisse tre excut. Bien entendu,
il est possible dajouter vos propres mthodes une classe et de les appeler partir de la mthode
main (vous verrez au prochain chapitre comment crire vos propres mthodes).
INFO
Selon la spcification du langage Java, la mthode main doit tre dclare public. La spcification est le document
officiel qui dcrit le langage Java. Vous pouvez le consulter ou le tlcharger ladresse http://java.sun.com/docs/
books/jls. Toutefois, plusieurs versions du lanceur Java avaient pour intention dexcuter les programmes Java mme
lorsque la mthode main ntait pas public. Un programmeur a alors rdig un rapport de bogue. Pour le voir,
consultez le site http://bugs.sun.com/bugdatabase/index.jsp et entrez le numro didentification 4252539. Le bogue
a toutefois fait lobjet de la mention "clos, ne sera pas rsolu". Un ingnieur Sun a ajout une explication indiquant
que la spcification de la machine virtuelle ( ladresse http://java.sun.com/docs/books/vmspec) nobligeait pas ce
que main soit public et a prcis que "le rsoudre risquait dentraner des problmes". Heureusement, le bon sens
a fini par parler. Le lanceur Java du JDK 1.4 et au-del oblige ce que la mthode main soit public.
Cette histoire est assez intressante. Dun ct, il est dsagrable de voir que des ingnieurs dassurance qualit,
souvent dbords et pas toujours au fait des aspects pointus de Java, prennent des dcisions contestables sur les
rapports de bogues. De lautre, il est remarquable que Sun place les rapports de bogues et leurs rsolutions sur le
Web, afin que tout le monde puisse les tudier. La "parade des bogues" est une ressource trs utile pour les programmeurs. Vous pouvez mme "voter" pour votre bogue favori. Ceux qui runiront le plus de suffrages pourraient bien
tre rsolus dans la prochaine version du JDK.

Remarquez les accolades dans le code source. En Java, comme en C/C++, les accolades sont employes
pour dlimiter les parties (gnralement appeles blocs) de votre programme. En Java, le code de chaque
mthode doit dbuter par une accolade ouvrante { et se terminer par une accolade fermante }.
La manire demployer des accolades a provoqu une inpuisable controverse. Nous employons
dans cet ouvrage un style dindentation classique, en alignant les accolades ouvrantes et fermantes
de chaque bloc. Comme les espaces ne sont pas pris en compte par le compilateur, vous pouvez utiliser le style de prsentation que vous prfrez. Nous reparlerons de lemploi des accolades lorsque
nous tudierons les boucles.
Pour linstant, ne vous proccupez pas des mots cl static void, songez simplement quils sont
ncessaires la compilation du programme. Cette curieuse incantation vous sera familire la fin du
Chapitre 4. Rappelez-vous surtout que chaque application Java doit disposer dune mthode main
dont len-tte est identique celui que nous avons prsent dans notre exemple :
public class NomDeClasse
{
public static void main(String[] args)
{
Instructions du programme
}
}

Livre Java .book Page 46 Jeudi, 25. novembre 2004 3:04 15

46

Au cur de Java 2 - Notions fondamentales

INFO C++
Les programmeurs C++ savent ce quest une classe. Les classes Java sont comparables aux classes C++, mais certaines
diffrences risquent de vous induire en erreur. En Java, par exemple, toutes les fonctions sont des mthodes dune
classe quelconque (la terminologie standard les appelle des mthodes et non des fonctions membres). Ainsi, Java
requiert que la mthode main se trouve dans une classe. Sans doute tes-vous galement familiaris avec la notion
de fonction membre statique en C++. Il sagit de fonctions membres dfinies lintrieur dune classe et qui
noprent pas sur des objets. En Java, la mthode main est toujours statique. Prcisons enfin que, comme en C/C++,
le mot cl void indique que la mthode ne renvoie aucune valeur. Contrairement C/C++, la mthode main ne
renvoie pas un "code de sortie" au systme dexploitation. Si la mthode main se termine normalement, le
programme Java a le code de sortie 0 qui lindique. Pour terminer le programme avec un code de sortie diffrent,
utilisez la mthode System.exit.

Portez maintenant votre attention sur ce fragment de code :


{
System.out.println("We will not use Hello, World!");
}

Les accolades dlimitent le dbut et la fin du corps de la mthode. Celle-ci ne contient quune seule
instruction. Comme dans la plupart des langages de programmation, vous pouvez considrer les
instructions Java comme les phrases du langage. En Java, chaque instruction doit se terminer par un
point-virgule. En particulier, les retours la ligne ne dlimitent pas la fin dune instruction, et une
mme instruction peut donc occuper plusieurs lignes en cas de besoin.
Le corps de la mthode main contient une instruction qui envoie une ligne de texte vers la console.
Nous employons ici lobjet System.out et appelons sa mthode println. Remarquez que le point
sert invoquer la mthode. Java utilise toujours la syntaxe
objet.mthode(paramtres)

pour ce qui quivaut un appel de fonction.


Dans ce cas prcis, nous appelons la mthode println et lui passons une chane en paramtre. La
mthode affiche la chane sur la console. Elle passe ensuite la ligne afin que chaque appel
println affiche la chane spcifie sur une nouvelle ligne. Notez que Java, comme C/C++, utilise les
guillemets pour dlimiter les chanes (pour plus dinformations, voir la section de ce chapitre consacre
aux chanes).
Comme les fonctions de nimporte quel langage de programmation, les mthodes de Java peuvent
utiliser zro, un ou plusieurs paramtres (certains langages les appellent arguments). Mme si une
mthode ne prend aucun paramtre, il faut nanmoins employer des parenthses vides). Il existe par
exemple une variante sans paramtres de la mthode println qui imprime une ligne vide. Elle est
invoque de la faon suivante :
System.out.println();

INFO

System.out dispose galement dune mthode print qui najoute pas de retour la ligne en sortie. Par exemple,
System.out.print("Bonjour") imprime "Bonjour" sans retourner la ligne. La sortie suivante apparatra
immdiatement derrire le "r" de "Bonjour".

Livre Java .book Page 47 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

47

Commentaires
Les commentaires de Java, comme ceux de la plupart des langages de programmation, napparaissent pas dans le programme excutable. Vous pouvez donc ajouter autant de commentaires que vous
le souhaitez sans craindre de gonfler la taille du code compil. Java propose trois types de commentaires. Le plus courant est //, qui dbute un commentaire allant jusqu la fin de ligne :
System.out.println("We will not use Hello, World!");
// Ce gag est-il trop connu?

Lorsque des commentaires plus longs sont ncessaires, il est possible de placer // au dbut de
chaque ligne, ou vous pouvez utiliser /* et */ pour dlimiter le dbut et la fin dun long commentaire.
Nous voyons ce type de dlimiteur lExemple 3.1.
Exemple 3.1 : FirstSample.java
/*
Voici le premier exemple de programme de Au coeur de Java, Chapitre3
Copyright (C) 1997 Cay Horstmann et Gary Cornell
*/
public class FirstSample
{
public static void main(String[] args)
{
System.out.println("We will not use Hello, World!");
}
}

Il existe un troisime type de commentaire utilisable pour la gnration automatique de documentation. Ce type de commentaire commence par /** et se termine par */. Pour en savoir plus sur la
gnration automatique de documentation, consultez le Chapitre 4.
ATTENTION
Les commentaires /* */ ne peuvent pas tre imbriqus en Java. Autrement dit, pour dsactiver un bloc de code, il
ne suffit pas de lenfermer entre un /* et un */, car ce bloc peut lui-mme contenir un dlimiteur */.

Types de donnes
Java est un langage fortement typ. Cela signifie que le type de chaque variable doit tre dclar.
Il existe huit types primitifs (prdfinis) en Java. Quatre dentre eux sont des types entiers (integer) ;
deux sont des types rels virgule flottante ; un est le type caractre char utilis pour le codage
Unicode (voir la section consacre au type char) et le type boolean, pour les valeurs boolennes
(vrai/faux).
INFO
Java dispose dun package arithmtique de prcision arbitraire. Cependant, les "grands nombres", comme on les
appelle, sont des objets Java et ne constituent pas un nouveau type Java. Vous apprendrez les utiliser plus loin dans
ce chapitre.

Livre Java .book Page 48 Jeudi, 25. novembre 2004 3:04 15

48

Au cur de Java 2 - Notions fondamentales

Entiers
Les types entiers reprsentent les nombres sans partie dcimale. Les valeurs ngatives sont autorises.
Java dispose des quatre types prsents au Tableau 3.1.
Tableau 3.1 : Les types entiers de Java

Type

Occupation
en mmoire

Intervalle (limites incluses)

int

4 octets

2 147 483 648 2 147 483 647 (un peu plus de 2 milliards)

short

2 octets

32768 32767

long

8 octets

9 223 372 036 854 775 808 9 223 372 036 854 775 807

byte

1 octet

128 127

Le type int se rvle le plus pratique dans la majorit des cas. Bien entendu, si vous dsirez exprimer le nombre des habitants de la plante, vous devrez employer le type long. Les types byte et
short sont essentiellement destins des applications spcialises, telles que la gestion bas niveau
des fichiers ou la manipulation de tableaux volumineux, lorsque loccupation mmoire doit tre
rduite au minimum.
En Java, la plage valide des types de nombres entiers ne dpend pas de la machine sur laquelle
sexcute le code. Cela pargne bien des efforts au programmeur souhaitant porter un logiciel
dune plate-forme vers une autre, ou mme entre diffrents systmes dexploitation sur une
mme plate-forme. En revanche, les programmes C et C++ utilisent le type dentier le plus efficace pour chaque processeur. Par consquent, un programme C qui fonctionne bien sur un
processeur 32 bits peut provoquer un dpassement de capacit sur un systme 16 bits. Comme
les programmes Java doivent sexcuter identiquement sur toutes les machines, les plages de
valeur des diffrents types sont fixes.
Le suffixe des entiers longs est L (par exemple, 4000000000L). Le prfixe des entiers hexadcimaux
est 0x (par exemple, 0xCAFE). Les valeurs octales ont le prfixe 0. Par exemple, 010 vaut 8. Cela peut
prter confusion, il est donc dconseill davoir recours aux constantes octales.

INFO C++
En C et C++, int reprsente le type entier qui dpend de lordinateur cible. Sur un processeur 16 bits, comme le 8086,
les entiers sont cods sur 2 octets. Sur un processeur 32 bits, comme le SPARC de Sun, ils sont cods sur 4 octets. Sur
un Pentium Intel, le codage du type entier C et C++ dpend du systme dexploitation : 2 octets sous DOS et
Windows 3.1, 4 octets sous Windows en mode 32 bits. En Java, la taille de tous les types numriques est indpendante de la plate-forme utilise.
Remarquez que Java ne possde pas de type non sign.

Livre Java .book Page 49 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

49

Types virgule flottante


Les types virgule flottante expriment les nombres rels disposant dune partie dcimale. Il existe
deux types de rels, prsents au Tableau 3.2.
Tableau 3.2 : Les types virgule flottante

Type

Occupation
en mmoire

Intervalle

float

4 octets

Environ 3.40282347E + 38F (6 ou 7 dcimales significatives)

double

8 octets

Environ 1.79769313486231570E + 308 (15 chiffres significatifs)

Le terme double indique que les nombres de ce type ont une prcision deux fois suprieure ceux
du type float (on les appelle parfois nombres double prcision). On choisira de prfrence le type
double dans la plupart des applications. La prcision limite de float se rvle insuffisante dans de
nombreuses situations. On ne lemploiera que dans les rares cas o la vitesse de calcul (plus leve
pour les nombres prcision simple) est importante pour lexcution, ou lorsquune grande quantit
de nombres doit tre stocke (afin dconomiser la mmoire).
Les nombres de type float ont pour suffixe F, par exemple 3.402F. Les nombres dcimales exprims sans ce suffixe F, par exemple 3.402, sont toujours considrs comme tant du type double.
Pour ces derniers, il est galement possible de spcifier un suffixe D, par exemple 3.402D.
Depuis le JDK 5.0, vous pouvez spcifier des nombres virgule flottante en hexadcimal. Par exemple, 0.125 quivaut 0x1.0p-3. Dans la notation hexadcimale, vous utilisez p, et non e, pour indiquer
un exposant.
Tous les calculs en virgule flottante respectent la spcification IEEE 754. En particulier, il existe trois
valeurs spciales en virgule flottante :
m

infinit positive ;

infinit ngative ;

NaN (Not a Number pas un nombre).

Elles servent indiquer les dpassements et les erreurs. Par exemple, le rsultat de la division dun
nombre positif par 0 est "infinit positive". Le calcul 0/0 ou la racine carre dun nombre ngatif
donne "NaN".
INFO
Il existe des constantes Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY et Double.NaN (ainsi que
les constantes correspondantes Float) permettant de reprsenter ces valeurs spciales. Elles sont cependant rarement
utilises dans la pratique. En particulier, vous ne pouvez tester
if (x == Double.NaN) // nest jamais vrai

pour vrifier si un rsultat particulier est gal Double.NaN. Toutes les valeurs "pas un nombre" sont considres
comme distinctes. Vous pouvez cependant employer la mthode Double.isNaN:
if (Double.isNaN(x)) // vrifier si x est "pas un nombre"

Livre Java .book Page 50 Jeudi, 25. novembre 2004 3:04 15

50

Au cur de Java 2 - Notions fondamentales

ATTENTION
Les nombres virgule flottante ne conviennent pas aux calculs financiers, dans lesquels les erreurs darrondi sont inadmissibles. La commande System.out.println(2.0 - 1.1) produit 0.8999999999999999, et non 0.9 comme
vous pourriez le penser. Ces erreurs darrondi proviennent du fait que les nombres virgule flottante sont reprsents
dans un systme de nombres binaires. Il nexiste pas de reprsentation binaire prcise de la fraction 1/10, tout comme
il nexiste pas de reprsentation prcise de la fraction 1/3 dans le systme dcimal. Si vous voulez obtenir des calculs
numriques prcis sans erreurs darrondi, utilisez la classe BigDecimal, prsente plus loin dans ce chapitre.

Le type char
Pour bien comprendre le type char, vous devez connatre le schma de codage Unicode. Unicode a
t invent pour surmonter les limitations des schmas de codage traditionnels. Avant lui, il existait
de nombreuses normes diffrentes : ASCII aux Etats-Unis, ISO 8859-1 pour les langues dEurope de
lEst, KOI-8 pour le russe, GB18030 et BIG-5 pour le chinois, etc. Deux problmes se posaient. Une
valeur de code particulire correspondait des lettres diffrentes selon le schma de codage. De
plus, le codage des langues ayant de grands jeux de caractres avait des longueurs variables :
certains caractres communs taient cods sous la forme doctets simples, dautres ncessitaient
deux octets ou plus.
Unicode a t conu pour rsoudre ces problmes. Aux dbuts des efforts dunification, dans les
annes 1980, un code de largeur fixe sur 2 octets tait plus que suffisant pour coder tous les caractres utiliss dans toutes les langues du monde, et il restait encore de la place pour une future extension
(ou, du moins, cest ce que lon pensait). En 1991 est sorti Unicode 1.0, qui utilisait lgrement
moins de la moiti des 65 536 valeurs de code disponibles. Java a t conu ds le dpart pour utiliser les caractres Unicode 16 bits, ce qui constituait une avance majeure sur les autres langages de
programmation, qui utilisaient des caractres sur 8 bits.
Malheureusement, au fil du temps, ce qui devait arriver arriva. Unicode a grossi au-del des
65 536 caractres, principalement du fait de lajout dun trs grand jeu didogrammes utiliss
pour le chinois, le japonais et le coren. Le type char 16 bits est dsormais insuffisant pour
dcrire tous les caractres Unicode.
Nous ferons appel la terminologie pour expliquer comment ce problme a t rsolu en Java, et ce
depuis le JDK 5.0. Un point de code est une valeur de code associe un caractre dans un schma
de codage. Dans la norme Unicode, les points de code sont crits en hexadcimal et prfixs par U+,
par exemple U+0041 pour le point de code de la lettre A. Unicode possde des points de code regroups en 17 plans de codes. Le premier, appel plan multilingue de base, est constitu des caractres
Unicode "classiques" ayant les points de code U+0000 U+FFFF. Seize autres plans, ayant les points
de code U+10000 U+10FFFF, contiennent les caractres complmentaires.
Le codage UTF-16 permet de reprsenter tous les points de code Unicode dans un code de longueur
variable. Les caractres du plan multilingue de base sont reprsents sous forme de valeurs de
16 bits, appeles units de code. Les caractres complmentaires sont englobs sous forme de paires
conscutives dunits de code. Chacune des valeurs dune paire de codage se situe dans une plage
inusite de 2 048 octets du plan multilingue de base, appel zone de remplacement (de U+D800
U+DBFF pour la premire unit de code, de U+DC00 U+DFFF pour la deuxime unit de code). Ceci
est assez intressant, car vous pouvez immdiatement dire si une unit de code procde un codage
sur un seul caractre ou sil sagit de la premire ou de la deuxime partie dun caractre complmentaire. Par exemple, le symbole mathmatique pour le jeu dentiers a le point de code U+1D56B et

Livre Java .book Page 51 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

51

est cod par les deux units de code U+D835 et U+DD6B (voir http://en.wikipedia.org/wiki/UTF-16
pour obtenir une description de lalgorithme de codage).
En Java, le type char dcrit une unit de code en codage UTF-16.
Nous recommandons fortement de ne pas utiliser le type char dans vos programmes, moins dtre
laise dans la manipulation des units de code UTF-16. Il vaut presque mieux traiter les chanes (ce
que nous verrons plus loin) sous forme de types de donnes abstraits.
Ceci tant dit, vous risquez tout de mme de rencontrer des valeurs char. Le plus souvent, ce seront
des constantes de caractres. Par exemple A est une constante de caractre de valeur 65. Elle diffre
de "A", une chane contenant un seul caractre. Les units de code Unicode peuvent tre exprimes
sous la forme hexadcimale, de \u0000 \uFFFF. Par exemple, \u2122 reprsente le symbole de
marque dpose ().
En plus du caractre dchappement \u indiquant le codage dune unit de code Unicode,
plusieurs squences dchappement existent pour les caractres spciaux cits dans le
Tableau 3.3. Vous pouvez les utiliser dans des constantes et des chanes de caractres entre apostrophes ou guillemets, comme \u2122 ou "Hello\n". La squence dchappement \u (mais
elle seule) peut mme tre utilise en dehors des constantes et des chanes entre apostrophes ou
guillemets. Par exemple,
public static void main(String\u005B\u005D args)

est tout fait autoris : \u005B et \u005D sont les codages UTF-16 des points de code Unicode pour
[ et ].
Tableau 3.3 : Squences dchappement pour caractres spciaux

Code dchappement

Dsignation

Valeur Unicode

\b

Effacement arrire

\u0008

\t

Tabulation horizontale

\u0009

\n

Saut de ligne

\u000a

\r

Retour chariot

\u000d

\"

Guillemet

\u0022

Apostrophe

\u0027

\\

Antislash

\u005c

INFO
Bien que lon puisse thoriquement utiliser nimporte quel caractre Unicode dans une application ou un applet
Java, laffichage dun caractre dpend, en dfinitive, des capacits de votre systme dexploitation et ventuellement
de votre navigateur (pour un applet).

Livre Java .book Page 52 Jeudi, 25. novembre 2004 3:04 15

52

Au cur de Java 2 - Notions fondamentales

Type boolen
Le type boolean peut avoir deux valeurs, false (faux) ou true (vrai). Il est employ pour lvaluation de conditions logiques. Les conversions entre valeurs entires et boolennes sont impossibles.
INFO C++
En C++, des nombres et mme des pointeurs peuvent tre employs la place des valeurs boolennes. La valeur 0
quivaut la valeur boolenne false, et une valeur non zro quivaut true. Ce nest pas le cas avec Java.
Les programmeurs Java sont donc mis en garde contre ce type daccidents :
if (x = 0) // pardon... je voulais dire x == 0

En C++, ce test est compil et excut, il donne toujours le rsultat false. En Java, la compilation du test choue,
car lexpression entire x = 0 ne peut pas tre convertie en une valeur boolenne.

Variables
En Java, toute variable a un type. Vous dclarez une variable en spcifiant dabord son type, puis son
nom. Voici quelques exemples de dclarations :
double salary;
int vacationDays;
long earthPopulation;
boolean done;

Remarquez que chaque dclaration se termine par un point-virgule. Il est ncessaire, car une dclaration est une instruction Java complte.
Un nom de variable doit dbuter par une lettre et doit tre une squence de lettres ou de chiffres.
Notez que le sens des termes "lettres" et "chiffres" est plus large en Java que dans beaucoup dautres
langages. Une lettre est dfinie comme lun des caractres A...Z, a...z, _, ou
nimporte quel caractre Unicode reprsentant une lettre dans une langue quelconque. Par exemple,
un utilisateur franais ou allemand peut employer des caractres accentus, tels que ou , dans un
nom de variable. De mme, les lecteurs grecs peuvent utiliser . Dune manire comparable, un
chiffre est un des caractres 0 9, ou nimporte quel caractre Unicode reprsentant un chiffre
dans une langue. Des espaces ou des symboles tels que + ou ne peuvent pas tre employs.
Tous les caractres dun nom de variable sont significatifs, ainsi que la casse de chaque caractre
(minuscule ou majuscule). La longueur possible dun nom est thoriquement illimite.
ASTUCE
Si vous vous demandez quels caractres Unicode sont des "lettres" pour Java, vous pouvez le savoir en appelant les
mthodes isJavaIdentifierStart et isJavaIdentifierPart de la classe Character.

Il est videmment interdit de donner une variable le mme nom quun mot rserv de Java (voir la
liste des mots rservs dans lAnnexe A).
Il est possible de dclarer plusieurs variables sur une seule ligne, comme suit :
int i, j; // ce sont deux entiers en Java

Livre Java .book Page 53 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

53

Ce style de dclaration nest pas recommand. Si vous dfinissez chaque variable sparment, vos
programmes seront plus faciles lire.
INFO
Vous avez vu que les noms taient sensibles la casse. Par exemple, hireday et hireDay sont deux noms diffrents.
En rgle gnrale, nemployez pas deux noms diffrant seulement par la casse des caractres. Il est toutefois difficile
parfois de dfinir un nom correct pour une variable. Les programmeurs attribuent alors frquemment la variable
le mme nom que le type, par exemple :
Box box; // OK--Box est le type et box le nom de la variable

Cependant, une meilleure solution consiste attribuer un prfixe "a" la variable


Box aBox;.

Initialisation des variables


Aprs avoir dclar une variable, vous devez explicitement linitialiser laide dune instruction
daffectation. Vous ne devez pas utiliser une variable non initialise. Le compilateur Java signale la
suite dinstructions suivantes comme une erreur :
int vacationDays;
System.out.println(vacationDays); //ERREUR--variable non-initialise

Laffectation dune variable dclare se fait laide du symbole dgalit (=), prcd gauche du
nom de la variable et suivi droite par une expression Java reprsentant la valeur approprie :
int vacationDays;
vacationDays = 12;

Une agrable fonctionnalit de Java permet de dclarer et dinitialiser simultanment une variable en
une seule instruction, de la faon suivante :
int vacationDays = 12;

Prcisons enfin que Java permet de dclarer des variables nimporte o dans le code. Par exemple, le
code suivant est valide en Java :
double salary = 65000.0;
System.out.println(salary);
int vacationDays = 12; // vous pouvez dclarer la variable ici

En Java, il est recommand de dclarer les variables aussi prs que possible du point de leur
premire utilisation.
INFO C++
C et C++ font une distinction entre une dclaration et une dfinition de variables.
Par exemple,
int i = 10;

est une dfinition, alors que


extern int i;

est une dclaration. En Java, il ny a pas de dclaration spare de la dfinition.

Livre Java .book Page 54 Jeudi, 25. novembre 2004 3:04 15

54

Au cur de Java 2 - Notions fondamentales

Constantes
En Java, le mot cl final sert dsigner une constante. Voici un exemple :
public class Constants
{
public static void main(String[] args)
{
final double CM_PER_INCH = 2.54;
double paperWidth = 8.5;
double paperHeight = 11;
System.out.println("Paper size in centimeters: "
+paperWidth * CM_PER_INCH+" by "
+paperHeight * CM_PER_INCH);
}
}

Le mot cl final signifie que vous affectez une valeur la variable une seule fois, et une fois pour
toutes. Par convention, les noms des constantes sont entirement en majuscules.
Il est plus courant de crer une constante qui est accessible plusieurs mthodes de la mme classe.
Les mthodes de ce genre sont appeles gnralement constantes de classe. On dfinit une constante
de classe laide des mots cls static final. Voici un exemple utilisant une constante de classe :
public class Constants2
{
public static void main(String[] args)
{
double paperWidth = 8.5;
double paperHeight = 11;
System.out.println("Paper size in centimeters: "
+paperWidth * CM_PER_INCH+" by "
+paperHeight * CM_PER_INCH);
}
public static final double CM_PER_INCH = 2.54;
}

Notez que la dfinition de la constante de classe apparat en dehors de la mthode main. La constante
peut donc aussi tre employe dans dautres mthodes de la mme classe. De plus, si (comme dans
notre exemple), la constante est dclare public, les mthodes des autres classes peuvent aussi utiliser
la constante sous la forme Constants2.CM_PER_INCH, dans notre exemple.
INFO C++
Bien que const soit un mot rserv de Java, il nest pas toujours utilis. Cest le mot cl final qui permet de dfinir
une constante.

Oprateurs
Les habituels oprateurs arithmtiques + * / sont respectivement utiliss en Java pour les oprations daddition, de soustraction, de multiplication et de division. Loprateur de division / indique
une division entire si les deux arguments sont des entiers et une division en virgule flottante dans les

Livre Java .book Page 55 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

55

autres cas. Le reste dune division entire est reprsent par le symbole %. Par exemple, 15/2 donne
7; 15% 2 donne 1 et 15.0/2 donne 7.5.
Notez que la division dun entier par 0 dclenche une exception, alors que la division dune valeur en
virgule flottante par 0 donne un rsultat infini ou NaN.
Les oprateurs arithmtiques peuvent tre utiliss en combinaison avec loprateur daffectation
pour simplifier lcriture dune affectation. Par exemple, linstruction
x += 4;

quivaut linstruction
x = x+4;

En rgle gnrale, placez loprateur arithmtique gauche du signe =, par exemple *= ou %=.
INFO
Lun des intrts vidents de la programmation en langage Java est la portabilit. Un calcul doit aboutir au mme
rsultat quelle que soit la machine virtuelle sur laquelle il est excut. Dans le cas de calculs avec des nombres
virgule flottante, il est tonnamment difficile dobtenir cette portabilit. Le type double utilise 64 bits pour le
stockage dune valeur numrique, mais certains processeurs emploient des registres virgule flottante de 80 bits.
Ces registres ajoutent une prcision lors des tapes intermdiaires dun calcul. Examinez, par exemple, le calcul
suivant :
double w = x * y / z;

De nombreux processeurs Intel vont calculer x * y et placer le rsultat dans un registre 80 bits, puis diviser par z
pour finalement tronquer le rsultat 64 bits. Cela peut donner un rsultat plus prcis et viter un dpassement
dexposant. Cependant le rsultat peut tre diffrent de celui dun calcul effectu constamment sur 64 bits. Pour
cette raison, la spcification initiale de la machine virtuelle Java prvoyait la troncation de tous les calculs intermdiaires, au grand dam de la communaut numrique. Non seulement les calculs tronqus peuvent provoquer un
dpassement de capacit, mais ils sont aussi plus lents, du fait du temps ncessaire aux oprations de troncation.
Cest pourquoi le langage Java a t actualis pour tenir compte des besoins conflictuels de performance optimale
et de reproductibilit parfaite. Par dfaut, les concepteurs de machine virtuelle peuvent maintenant utiliser une
prcision tendue pour les calculs intermdiaires. Toutefois, les mthodes balises avec le mot cl strictfp doivent
utiliser des oprations virgule flottante strictes menant des rsultats reproductibles. Vous pouvez, par exemple,
baliser la mthode main de la faon suivante :
public static strictfp void main(String[] args)

Toutes les instructions au sein de main auront alors recours des calculs virgule flottante stricts. Si vous balisez une
classe laide de strictfp, toutes ses mthodes utiliseront les calculs virgule flottante stricts.
Les dtails sordides dpendent essentiellement du comportement des processeurs Intel. En mode par dfaut, les
rsultats intermdiaires sont autoriss utiliser un exposant tendu, mais pas une mantisse tendue (les puces Intel
grent la troncation de la mantisse sans perte de performance). Par consquent, la seule diffrence entre les modes
par dfaut et strict est que les calculs stricts peuvent engendrer des dpassements de capacit, la diffrence des
calculs par dfaut.
Si vos yeux sarrondissent la lecture de cette Info, ne vous inquitez pas. Le dpassement de capacit en virgule
flottante ne se pose pas pour la majorit des programmes courants. Nous nutiliserons pas le mot cl strictfp dans
cet ouvrage.

Livre Java .book Page 56 Jeudi, 25. novembre 2004 3:04 15

56

Au cur de Java 2 - Notions fondamentales

Oprateurs dincrmentation et de dcrmentation


Les programmeurs savent videmment quune des oprations les plus courantes effectues sur une
variable numrique consiste lui ajouter ou lui retrancher 1. Suivant les traces de C et de C++, Java
offre des oprateurs dincrmentation et de dcrmentation : x++ ajoute 1 la valeur courante et x-retranche 1 cette valeur. Ainsi, cet exemple :
int n = 12;
n++;

donne n la valeur 13. Comme ces oprateurs modifient la valeur dune variable, ils ne peuvent pas
tre appliqus des nombres. Par exemple, 4++ nest pas une instruction valide.
Ces oprateurs peuvent en ralit prendre deux formes ; vous avez vu la forme "suffixe", o loprateur est situ aprs loprande : n++. Il peut galement tre plac en prfixe : ++n. Dans les deux cas,
la variable est incrmente de 1. La diffrence entre ces deux formes napparat que lorsquelles sont
employes dans des expressions. Lorsquil est plac en prfixe, loprateur effectue dabord laddition ;
en suffixe, il fournit lancienne valeur de la variable :
int
int
int
int

m
n
a
b

=
=
=
=

7;
7;
2 * ++m; // maintenant a vaut 16, m vaut 8
2 * n++; // maintenant b vaut 14, n vaut 8

Nous dconseillons vivement lemploi de loprateur ++ dans dautres expressions, car cela entrane
souvent un code confus et des bogues difficiles dtecter.
Loprateur ++ a bien videmment donn son nom au langage C++, mais il a galement provoqu une des
premires plaisanteries propos de ce langage. Les anti-C++ ont fait remarquer que mme le nom du
langage contenait un bogue : "En fait, il devrait sappeler ++C, car on ne dsire employer le
langage quaprs son amlioration".

Oprateurs relationnels et boolens


Java offre le jeu complet doprateurs relationnels. Un double signe gal, ==, permet de tester
lgalit de deux oprandes. Par exemple, lvaluation de
3 == 7

donne un rsultat faux (false).


Utilisez != pour pratiquer un test dingalit. Par exemple, le rsultat de
3!= 7

est vrai (true).


De plus, nous disposons des habituels oprateurs < (infrieur ), > (suprieur ), <= (infrieur ou gal
) et >= (suprieur ou gal ).
Suivant lexemple de C++, Java utilise && comme oprateur "et" logique et || comme oprateur "ou"
logique. Le point dexclamation ! reprsente loprateur de ngation. Les oprateurs && et || sont
valus de manire optimise (en court-circuit). Le deuxime argument nest pas valu si le
premier dtermine dj la valeur. Si vous combinez deux expressions avec loprateur &&,
expression && expression

Livre Java .book Page 57 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

57

la valeur de la deuxime expression nest pas calcule si la premire a pu tre value false (puisque le rsultat final serait false de toute faon). Ce comportement peut viter des erreurs. Par exemple,
dans lexpression
x!= 0 && 1 / x > x+y // pas de division par 0

la seconde partie nest jamais value si x gale zro. Par consquent, 1/x nest pas calcul si x vaut
zro, et une erreur "division par zro" ne peut pas se produire.
De mme, si la premire expression est value true, la valeur de expression1 || expression2
est automatiquement true, sans que la deuxime expression doive tre value.
Enfin, Java gre loprateur ternaire ? qui se rvle utile loccasion. Lexpression
condition? expression1: expression2

est value expression1 si la condition est true, expression2 sinon. Par exemple,
x < y? x: y

donne le plus petit entre x et y.

Oprateurs binaires
Lorsquon manipule des types entiers, il est possible demployer des oprateurs travaillant directement sur les bits qui composent ces entiers. Certaines techniques de masquage permettent de rcuprer
un bit particulier dans un nombre. Les oprateurs binaires sont les suivants :
&("et")

|("ou")

^("ou exclusif")

~("non")

Ces oprateurs travaillent sur des groupes de bits. Par exemple, si n est une variable entire, alors la
dclaration
int quatrimeBitPartantDeDroite = (n & 8) / 8;

renvoie 1 si le quatrime bit partir de la droite est 1 dans la reprsentation binaire de n, et zro
dans le cas contraire. Lemploi de & avec la puissance de deux approprie permet de masquer tous les
bits sauf un.
INFO
Lorsquils sont appliqus des valeurs boolean, les oprateurs & et | donnent une valeur boolean. Ces oprateurs
sont comparables aux oprateurs && et ||, except que & et | ne sont pas valus de faon optimise "court-circuit".
Cest--dire que les deux arguments sont valus en premier, avant le calcul du rsultat.

Il existe galement des oprateurs >> et << permettant de dcaler un groupe de bits vers la droite ou
vers la gauche. Ces oprateurs se rvlent pratiques lorsquon veut construire des masques binaires :
int quatrimeBitPartantDeDroite = (n & (1 << 3)) >> 3;

Signalons enfin quil existe galement un oprateur >>> permettant de remplir les bits de poids fort
avec des zros, alors que >> tend le bit de signature dans les bits de poids fort. Il nexiste pas
doprateur <<<.

Livre Java .book Page 58 Jeudi, 25. novembre 2004 3:04 15

58

Au cur de Java 2 - Notions fondamentales

ATTENTION
Largument droite des oprateurs de dcalage est rduit en modulo 32 ( moins quil ny ait un type long du
ct gauche, auquel cas le ct droit est rduit en modulo 64). Par exemple, la valeur de 1 << 35 est la mme
que 1 << 3 soit 8.

INFO C++
En C/C++, rien ne vous garantit que >> accomplit un dcalage arithmtique (en conservant le signe) ou un dcalage
logique (en remplissant les bits avec des zros). Les concepteurs de limplmentation sont libres de choisir le mcanisme le plus efficace. Cela signifie que loprateur >> de C/C++ nest rellement dfini que pour des nombres qui ne
sont pas ngatifs. Java supprime cette ambigut.

Fonctions mathmatiques et constantes


La classe Math contient un assortiment de fonctions mathmatiques qui vous seront parfois utiles,
selon le type de programmation que vous ralisez.
Pour extraire la racine carre dun nombre, vous disposez de la mthode sqrt:
double x = 4;
double y = Math.sqrt(x);
System.out.println(y); // affiche 2.0

INFO
Il existe une subtile diffrence entre les mthodes println et sqrt. La mthode println opre sur un objet,
System.out, dfini dans la classe System. Mais la mthode sqrt dans la classe Math nopre pas sur un objet. Une
telle mthode est qualifie de statique. Vous tudierez les mthodes statiques au Chapitre 4.

Le langage de programmation Java ne dispose pas doprateur pour lever une quantit une puissance :
vous devez avoir recours la mthode pow de la classe Math. Linstruction
double y = Math.pow(x, a);

dfinit y comme la valeur x leve la puissance a (xa). La mthode pow a deux paramtres qui sont
du type double, et elle renvoie galement une valeur double.
La classe Math fournit les fonctions trigonomtriques habituelles :
Math.sin
Math.cos
Math.tan
Math.atan
Math.atan2

et la fonction exponentielle et son inverse, le logarithme naturel :


Math.exp
Math.log

Il y a enfin deux constantes,


Math.PI
Math.E

qui donnent les approximations les plus proches possibles des constantes mathmatiques et e.

Livre Java .book Page 59 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

59

ASTUCE
Depuis le JDK 5.0, vous pouvez viter le prfixe Math pour les mthodes mathmatiques et les constantes en ajoutant la ligne suivante en haut de votre fichier source :
import static java.lang.Math.*;

Par exemple,
System.out.println("The square root of \u03C0 is " + sqrt(PI));

Nous verrons les importations statiques au Chapitre 4.

INFO
Les fonctions de la classe Math utilisent les routines dans lunit virgule flottante du calculateur pour une meilleure
performance. Si des rsultats totalement prvisibles sont plus importants que la rapidit, employez plutt la classe
StrictMath. Elle implmente les algorithmes provenant de la bibliothque mathmatique fdlibm "Freely Distributable Math Library", qui garantit des rsultats identiques quelle que soit la plate-forme. Consultez le site http://
www.netlib.org/fdlibm/index.html pour obtenir la source de ces algorithmes (alors que fdlibm fournit plus dune
dfinition pour une fonction, la classe StrictMath respecte la version IEEE 754 dont le nom commence par un "e").

Conversions de types numriques


Il est souvent ncessaire de convertir un type numrique en un autre. La Figure 3.1 montre les
conversions lgales :
Figure 3.1

char

Conversions lgales
entre types numriques.

byte

short

int

long

float

double

Les six flches noires la Figure 3.1 indiquent les conversions sans perte dinformation. Les trois
flches grises indiquent celles pouvant souffrir dune perte de prcision. Par exemple, un entier large
tel que 123456789 a plus de chiffres que ne peut en reprsenter le type float. Sil est converti en
type float, la valeur rsultante a la magnitude correcte, mais elle perd en prcision :
int n = 123456789;
float f = n; // f vaut 1.234567 92E8

Lorsque deux valeurs sont combines laide dun oprateur binaire (par exemple, n+f o n est un
entier et f une valeur virgule flottante), les deux oprandes sont convertis en un type commun avant
la ralisation de lopration :
m

Si lun quelconque des oprandes est du type double, lautre sera converti en type double.

Livre Java .book Page 60 Jeudi, 25. novembre 2004 3:04 15

60

Au cur de Java 2 - Notions fondamentales

Sinon, si lun quelconque des oprandes est du type float, lautre sera converti en type float.

Sinon, si lun quelconque des oprandes est du type long, lautre sera converti en type long.

Sinon les deux oprandes seront convertis en type int.

Transtypages
Dans la section prcdente, vous avez vu que les valeurs int taient automatiquement converties en
valeurs double en cas de besoin. Dautre part, il existe videmment des cas o vous voudrez considrer un double comme un entier. Les conversions numriques sont possibles en Java, mais bien
entendu, au prix dune perte possible dinformation. Les conversions risquant des pertes dinformation sont faites laide de transtypages (ou conversions de type). La syntaxe du transtypage fournit
le type cible entre parenthses, suivi du nom de la variable. Par exemple :
double x = 9.997;
int nx = (int)x;

La variable nx a alors la valeur 9, puisque la conversion dun type flottant en un type entier fait
perdre la partie fractionnaire.
Si vous voulez arrondir un nombre virgule flottante en lentier le plus proche (lopration la plus
utile dans la plupart des cas), utilisez la mthode Math.round:
double x = 9.997;
int nx = (int)Math.round(x);

Maintenant, la variable nx a la valeur 10. Vous devez toujours utiliser le transtypage (int) si vous
appelez round. Cest parce que la valeur renvoye par la mthode round est du type long et quun
long ne peut qutre affect un int avec un transtypage explicite, puisquil existe une possibilit
de perte dinformation.
ATTENTION
Si vous essayez de convertir un nombre dun type en un autre qui dpasse ltendue du type cible, le rsultat sera un
nombre tronqu ayant une valeur diffrente. Par exemple, (byte) 300 vaut en ralit 44.

INFO C++
Vous ne pouvez convertir des valeurs boolean en quelque type numrique que ce soit. Cela vite les erreurs courantes. Si, exceptionnellement, vous voulez convertir une valeur boolean en un nombre, vous pouvez avoir recours
une expression conditionnelle telle que b? 1: 0.

Parenthses et hirarchie des oprateurs


La hirarchie normale des oprations en Java est prsente au Tableau 3.4. En labsence de parenthses, les oprations sont ralises dans lordre hirarchique indiqu. Les oprateurs de mme niveau
sont traits de gauche droite, sauf pour ceux ayant une association droite, comme indiqu dans le
tableau. Par exemple, && ayant une priorit suprieure ||, lexpression
a && b || c

signifie
(a && b) || c

Livre Java .book Page 61 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

61

Etant donn que += a une associativit de droite gauche, lexpression


a += b += c

signifie
a += (b += c)

En fait, la valeur de b += c (qui est la valeur de b aprs laddition) est ajoute a.


Tableau 3.4 : Prsance des oprateurs

Oprateurs

Associativit

[] . () (appel de mthode)

De la gauche vers la droite

! ~ ++ --+ (unaire) - (unaire) () (transtypage) new

De la droite vers la gauche

* /%

De la gauche vers la droite

+ -

De la gauche vers la droite

<< >> >>>

De la gauche vers la droite

< <= > >= instanceof

De la gauche vers la droite

==!=

De la gauche vers la droite

&

De la gauche vers la droite

De la gauche vers la droite

De la gauche vers la droite

&&

De la gauche vers la droite

||

De la gauche vers la droite

?:

De la droite vers la gauche

= += -= *= /= %= &= |= ^= <<= >>= >>>=

De la droite vers la gauche

INFO C++
Contrairement C et C++, Java ne dispose pas dun oprateur "virgule". Il est nanmoins possible dutiliser une
liste dexpressions spares par des virgules comme premier ou troisime lment dune instruction for.

Types numrs
Une variable ne doit quelquefois contenir quun jeu de valeurs limit. Vous pouvez par exemple
vendre des vtements ou des pizzas en quatre formats : petit, moyen, grand, trs grand. Bien sr, ces
formats pourraient tre cods sous forme dentiers 1, 2, 3, 4 ou de caractres S, M, L et X. Mais cette
configuration est sujette erreur. Une variable peut trop facilement contenir une valeur errone
(comme 0 ou m).
Depuis le JDK 5.0, vous pouvez dfinir votre propre type numr ds quune situation se prsente.
Un type numr possde un nombre fini de valeurs nommes. Par exemple,
enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };

Livre Java .book Page 62 Jeudi, 25. novembre 2004 3:04 15

62

Au cur de Java 2 - Notions fondamentales

Vous pouvez maintenant dclarer des variables de ce type :


Size s = Size.MEDIUM;

Une variable de type Size ne peut contenir que lune des valeurs listes dans la dclaration de type
ou la valeur spciale null qui indique que la variable nest dfinie sur aucune valeur.
Nous verrons les types numrs plus en dtail au Chapitre 5.

Chanes
Les chanes sont des suites de caractres Unicode. Par exemple, la chane "Java\u2122" est constitue de cinq caractres Unicode J, a, v, a et . Java ne possde pas de type chane intgr. En revanche, la bibliothque standard Java contient une classe prdfinie appele, assez naturellement,
String. Chaque chane entre guillemets est une instance de la classe String:
String e = ""; // une chane vide
String greeting = "Hello";

Points et units de code


Les chanes Java sont implmentes sous forme de suites de valeurs char. Comme nous lavons vu,
le type char est une unit de code permettant de reprsenter des points de code Unicode en codage
UTF-16. Les caractres Unicode les plus souvent utiliss peuvent tre reprsents par une seule
unit de code. Les caractres complmentaires exigent quant eux une paire dunits de code.
La mthode length produit le nombre dunits de code exig pour une chane donne dans le
codage UTF-16. Par exemple,
String greeting = "Hello";
int n = greeting.length(); // vaut 5

Pour obtenir la bonne longueur, cest--dire le nombre de points de code, appelez


int cpCount = greeting.codePointCount(0, greeting.length());

Lappel s.chartAt(n) renvoie lunit de code prsente la position n, o n est compris entre 0 et
s.length() -1.
Par exemple,
char first = greeting.charAt(0); // le premier est H
char last = greeting.charAt(4); // le dernier est o

Pour accder au ime point de code, utilisez les instructions


int index = greeting.offsetByCodePoints(0, i);
int cp = greeting.codePointAt(index);

INFO
Java compte les units de code dans les chanes dune manire particulire : la premire unit de code dune chane
occupe la position 0. Cette convention est ne dans le C, une poque o il existait une raison technique dmarrer
les positions 0. Cette raison a disparu depuis longtemps, et seule la nuisance demeure. Toutefois, les programmeurs
sont tellement habitus cette convention que les concepteurs Java ont dcid de la garder.

Livre Java .book Page 63 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

63

Pourquoi nous proccuper tant des units de code ? Etudiez la phrase

Zis

the set of integers

Le caractre

Zexige deux units de code en codage UTF-16. Appeler

char ch = sentence.charAt(1)

ne renvoie pas un espace mais la deuxime unit de code de


mieux ne pas utiliser le type char, qui est de trop bas niveau.

Z. Pour viter ce problme, il vaut

Si votre code traverse une chane et que vous souhaitiez tudier chaque point de code tour tour,
utilisez ces instructions :
int cp = sentence.codePointAt(i);
if (Character.isSupplementaryCodePoint(cp)) i += 2;
else i++;

Heureusement, la mthode codePointAt peut dire si une unit de code est la premire ou la seconde
moiti dun caractre complmentaire, elle renvoie le bon rsultat quel que soit le cas. Ainsi, vous
pouvez revenir en arrire avec les instructions suivantes :
i--;
int cp = sentence.codePointAt(i);
if (Character.isSupplementaryCodePoint(cp)) i--;

Sous-chanes
Pour extraire une sous-chane partir dune chane plus grande, utilisez la mthode substring de la
classe String. Par exemple,
String greeting = "Hello";
String s = greeting.substring(0, 3);

cre une chane constitue des caractres "Hel".


Le deuxime paramtre de substring est la premire unit de code que vous ne souhaitez pas
copier. Ici, nous voulons copier les units de code des positions 0, 1 et 2 (de la position 0 la position
2 incluse). Pour substring, cela va de la position 0 incluse la position 3 (exclue).
On reconnat un avantage au fonctionnement de substring: il facilite le calcul du nombre dunits
de code prsentes dans la sous-chane. La chane s.substring(a, b) a toujours les units de code
b-a. Par exemple, la sous-chane "Hel" a 3 0 = 3 units de code.

Modification de chanes
La classe String ne fournit pas de mthode permettant de modifier un caractre dans une chane existante. Au cas o vous voudriez transformer le contenu de la variable greeting en "Help!", vous ne
pouvez pas remplacer directement la dernire position de greeting par p et par !. Si vous tes un
programmeur C, vous devez vous sentir frustr. Et, pourtant, la solution est trs simple en Java : il suffit
de rcuprer la sous-chane conserver et de la concatner avec les caractres remplacer :
greeting:= greeting.substring(0, 3)+ "p!";

Cette instruction donne la variable greeting la valeur "Help!".


Comme il nest pas possible de modifier individuellement des caractres dans une chane Java, la
documentation indique que les objets de la classe String sont inaltrables. Tout comme le nombre 3

Livre Java .book Page 64 Jeudi, 25. novembre 2004 3:04 15

64

Au cur de Java 2 - Notions fondamentales

vaut toujours 3, la chane "Hello" contient toujours la suite de caractres H, e, l, l, o.


Il est impossible de changer cette valeur. En revanche, comme nous venons de le voir, il est possible
de modifier le contenu de la variable chane greeting et de lui faire rfrencer une chane diffrente
(de mme quune variable numrique contenant la valeur 3 peut recevoir la valeur 4).
On pourrait penser que tout cela nest pas trs performant. Ne serait-il pas plus simple de changer les
units de code au lieu de construire une nouvelle chane ? Oui et non. En vrit, il nest pas trs intressant de gnrer une nouvelle chane contenant la concatnation de "Hel" et de "p!". Mais les
chanes inaltrables possdent pourtant un norme avantage : le compilateur peut les partager.
Pour comprendre cette technique, imaginez que les diverses chanes rsident dans un pool commun.
Les variables chanes, quant elles, pointent sur des positions dans le pool. Si vous copiez une variable chane, la chane dorigine et la copie partagent les mmes caractres. Tout bien considr, les
concepteurs de Java ont estim que lefficacit du partage des chanes surpassait linconvnient de la
modification des chanes par extraction de sous-chanes et concatnation.
Examinez vos propres programmes ; la plupart du temps, vous ne modifiez sans doute pas les chanes vous vous contentez de les comparer. Bien entendu, il existe des situations o une manipulation directe se rvle plus efficace (par exemple lorsquon assemble des chanes partir de caractres
individuels taps au clavier ou rcuprs dans un fichier). Pour ces cas-l, Java fournit la classe
StringBuilder, que nous dcrivons au Chapitre 12. Si la gestion des chanes ne vous proccupe
pas, vous pouvez ignorer StringBuilder et utiliser simplement la classe String.
INFO C++
Les programmeurs C sont souvent stupfaits lorsquils rencontrent des chanes Java pour la premire fois, car ils
considrent les chanes comme des tableaux de caractres :
char greeting[] = "Hello";

La comparaison nest pas bonne ; une chane Java ressemble davantage un pointeur char*:
char* greeting = "Hello";

Lorsque vous remplacez la valeur de greeting par une autre chane, le code Java excute peu prs ce qui suit :
char* temp = malloc(6);
strncpy(temp, greeting, 3);
strcpy(temp+4, "p!", 3);
greeting = temp;

greeting pointe maintenant vers la chane "Help!". Et mme le plus intgriste des programmeurs C doit
reconnatre que la syntaxe de Java est plus agrable quune srie dappels strncpy. Mais que se passe-t-il si
nous affectons une nouvelle valeur greeting?
greeting = "Howdy";

La chane dorigine ne va-t-elle pas occuper inutilement la mmoire, puisquelle a t alloue dans le pool ? Heureusement, Java rcupre automatiquement la mmoire libre. Si un bloc de mmoire nest plus utilis, il finira par tre
recycl.
Si vous programmez en C++ et utilisez la classe string dfinie par ANSI C++, vous vous sentirez laise avec le type
String de Java. Les objets string de C++ se chargent automatiquement de lallocation et de la rcupration de la
mmoire. La gestion de la mmoire est accomplie explicitement par les constructeurs, les oprateurs daffectations
et les destructeurs. Cependant, les chanes C++ sont altrables il est possible de modifier individuellement un
caractre dans une chane.

Livre Java .book Page 65 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

65

Concatnation
Java, comme la plupart des langages de programmation, autorise lemploi du signe + pour joindre
(concatner) deux chanes :
String expletive = "Expletive";
String PG13 = "deleted";
String message = expletive+PG13;

Le code qui prcde donne la variable message le contenu "Expletivedeleted" (remarquez quil
ninsre pas despace entre les mots : le signe + accole les deux chanes exactement telles quelles
sont fournies).
Lorsque vous concatnez une chane et une valeur qui nest pas une chane, cette valeur est convertie
en chane (comme vous le verrez au Chapitre 5, tout objet Java peut tre converti en chane). Voici un
exemple,
int age = 13;
String rating = "PG"+age;

qui donne la chane rating la valeur "PG13".


Cette caractristique est utilise couramment pour laffichage. Par exemple, linstruction
System.out.println("The answer is "+answer);

est parfaitement valide et affichera la rponse voulue avec un espacement correct (notez lespace
derrire le mot is).

Test dgalit des chanes


Pour savoir si deux chanes sont gales, utilisez la mthode equals; lexpression
s.equals(t)

donne true si les chanes s et t sont gales, et false dans le cas contraire. Notez que s et t peuvent
tre des variables chanes ou des constantes chanes. Par exemple,
"Hello".equals(greeting)

est parfaitement valide. Pour tester si deux chanes sont identiques lexception de la casse des
caractres, utilisez la mthode equalsIgnoreCase:
"Hello".equalsIgnoreCase("hello")

Attention, nemployez pas loprateur == pour tester lgalit de deux chanes ! Cet oprateur dtermine seulement si les chanes sont stockes au mme emplacement. Il est vident que si deux chanes se trouvent la mme adresse, elles doivent tre gales. Mais des copies de chanes identiques
peuvent tre stockes des emplacements diffrents dans la mmoire :
String greeting = "Hello"; //initialise la chane greeting
if (greeting == "Hello") . . .
// probablement vrai
if (greeting.substring(0, 3) == "Hel") . . .
// probablement faux

Si la machine virtuelle faisait en sorte de toujours partager les chanes identiques, loprateur ==
pourrait tre utilis pour un test dgalit. Mais seules les constantes chanes sont partages, et non
les chanes cres laide de loprateur + ou de la mthode substring. Par consquent,

Livre Java .book Page 66 Jeudi, 25. novembre 2004 3:04 15

66

Au cur de Java 2 - Notions fondamentales

nemployez jamais == pour comparer des chanes, car cela pourrait gnrer le pire des bogues : un
bogue intermittent qui semble se produire alatoirement.
INFO C++
Si vous tes familiaris avec la classe string de C++, vous devez tre particulirement attentif aux tests dgalit,
car la classe string surcharge loprateur == pour tester lgalit du contenu de deux chanes. Il est peut-tre
dommage que les chanes Java aient un "aspect gnral" comparable celui des valeurs numriques, mais quelles
se comportent comme des pointeurs lors des tests dgalit. Les concepteurs auraient pu redfinir == pour ladapter
aux chanes, comme ils lont fait pour loprateur +, mais aucun langage nest parfait.
Pour comparer des chanes, les programmeurs C nemploient pas loprateur ==, mais la fonction strcmp. La
mthode analogue en langage Java est compareTo. Vous pouvez crire
if (greeting.compareTo("Hello") == 0) . . .

mais il est plus pratique dappeler la mthode equals.

La classe String de Java contient plus de 50 mthodes. Beaucoup dentre elles sont assez utiles pour
tre employes couramment. La note API qui suit prsente les mthodes qui nous semblent les plus
intressantes pour le programmeur.
INFO
Vous rencontrerez ces notes API dans tout louvrage. Elles vous aideront comprendre linterface de programmation Java, ou API (Application Programming Interface). Chaque note API commence par le nom dune classe,
tel que java.lang.String la signification du nom de package java.lang sera explique au Chapitre 4. Le
nom de la classe est suivi des noms, explications et descriptions des paramtres dune ou plusieurs mthodes de
cette classe.
Nous ne donnons pas la liste de toutes les mthodes dune classe donne, mais seulement celles qui sont utilises le
plus frquemment, dcrites sous une forme concise. Consultez la documentation en ligne si vous dsirez obtenir une
liste complte des mthodes dune classe.
Nous indiquons galement le numro de version dans laquelle une classe particulire a t introduite. Si une
mthode a t ajoute par la suite, elle prsente un numro de version distinct.

java.util.HashSet

HashSet()

java.lang.String 1.0

char charAt(int index)

Renvoie lunit de code situe la position spcifie. Vous ne voudrez probablement pas appeler
cette mthode moins dtre intress par les units de code de bas niveau.

int codePointAt(int index) 5.0

Renvoie le point de code qui dmarre ou se termine lemplacement spcifi.

int offsetByCodePoints(int startIndex, int cpCount) 5.0

Renvoie lindice du point de code do pointe cpCount, depuis le point de code jusqu startIndex.

int compareTo(String other)

Livre Java .book Page 67 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

67

Renvoie une valeur ngative si la chane se trouve avant other (dans lordre alphabtique), une
valeur positive si la chane se trouve aprs other ou un 0 si les deux chanes sont identiques.

boolean endsWith(String suffix)

Renvoie true si la chane se termine par suffix.

boolean equals(Object other)

Renvoie true si la chane est identique other.

boolean equalsIgnoreCase(String other)

Renvoie true si la chane est identique other, sans tenir compte de la casse.

int indexOf(String str)


int indexOf(String str, int fromIndex)
int indexOf(int cp)
int indexOf(int cp, int fromIndex)

Renvoient la position de dpart de la premire sous-chane gale str ou au point de code cp,
en commenant par la position 0 ou par fromIndex ou 1 si str napparat pas dans cette chane.

int lastIndexOf(String str)


int lastIndexOf(String str, int fromIndex)
int lastIndexOf(int cp)
int lastIndexOf(int cp, int fromIndex)

Renvoient la position de dpart de la dernire sous-chane gale str ou au point de code cp, en
commenant la fin de la chane ou par fromIndex.

int length()

Renvoie la taille (ou longueur) de la chane.

int codePointCount(int startIndex, int endIndex) 5.0

Renvoie le nombre de points de code entre startIndex et endIndex-1. Les substitutions sans
paires sont considres comme des points de code.

String replace(CharSequence oldString, CharSequence newString)

Renvoie une nouvelle chane, obtenue en remplaant tous les caractres oldString de la chane
par les caractres newString. Vous pouvez fournir des objets String ou StringBuilder pour
les paramtres CharSequence.

boolean startsWith(String prefix)

Renvoie true si la chane commence par prefix.

String substring(int beginIndex)


String substring(int beginIndex, int endIndex)

Renvoient une nouvelle chane compose de toutes les units de code situes entre beginIndex
et, soit la fin de la chane, soit endIndex-1.

String toLowerCase()

Renvoie une nouvelle chane compose de tous les caractres de la chane dorigine, mais dont
les majuscules ont t converties en minuscules.

String toUpperCase()

Renvoie une nouvelle chane compose de tous les caractres de la chane dorigine, mais dont
les minuscules ont t converties en majuscules.

Livre Java .book Page 68 Jeudi, 25. novembre 2004 3:04 15

68

Au cur de Java 2 - Notions fondamentales

String trim()

Renvoie une nouvelle chane en liminant tous les espaces qui auraient pu se trouver devant ou
derrire la chane dorigine.

Lire la documentation API en ligne


Vous avez vu que la classe String comprend quantit de mthodes. Il existe de plus des centaines
de classes dans les bibliothques standard, avec bien dautres mthodes encore. Il est impossible de
mmoriser toutes les classes et mthodes utiles. Il est donc essentiel que vous puissiez facilement
consulter la documentation API en ligne concernant les classes et mthodes de la bibliothque standard. La documentation API fait partie du JDK. Elle est au format HTML. Pointez votre navigateur
Web sur le sous-rpertoire docs/api/index.html de votre installation JDK. Vous verrez apparatre
un cran comme celui de la Figure 3.2.
Figure 3.2
Les trois panneaux
de la documentation
API.

Lcran est divis en trois fentres. Une petite fentre en haut gauche affiche tous les packages
disponibles. Au-dessous, une fentre plus grande numre toutes les classes. Cliquez sur un nom
de classe pour faire apparatre la documentation API pour cette classe dans la fentre de droite
(voir Figure 3.3). Par exemple, pour obtenir des informations sur les mthodes de la classe
String, faites dfiler la deuxime fentre jusqu ce que le lien String soit visible, puis cliquez
dessus.
Faites dfiler la fentre de droite jusqu atteindre le rsum de toutes les mthodes, tries par ordre
alphabtique (voir Figure 3.4). Cliquez sur le nom dune mthode pour afficher sa description
dtaille (voir Figure 3.5). Par exemple, si vous cliquez sur le lien compareToIgnoreCase, vous
obtiendrez la description de la mthode compareToIgnoreCase.

Livre Java .book Page 69 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

Figure 3.3
Description
de la classe String.

Figure 3.4
Liste des mthodes
de la classe String.

ASTUCE
Affectez ds maintenant un signet la page docs/api/index.html dans votre navigateur.

69

Livre Java .book Page 70 Jeudi, 25. novembre 2004 3:04 15

70

Au cur de Java 2 - Notions fondamentales

Figure 3.5
Description dtaille
dune mthode
de la classe String.

Entres et sorties
Pour rendre nos programmes dexemple plus intressants, il faut accepter les saisies et mettre correctement en forme le programme. Bien entendu, les langages de programmation modernes utilisent
une interface graphique pour recueillir la saisie utilisateur. Mais la programmation de cette interface
exige plus doutils et de techniques que ce que nous avons disposition pour le moment. Lintrt
pour linstant tant de se familiariser avec le langage de programmation Java, nous allons nous
contenter de notre humble console pour lentre et la sortie. La programmation des interfaces
graphiques est traite aux Chapitres 7 9.

Lire les caractres entrs


Vous avez pu constater combien il tait simple dafficher une sortie sur lunit de "sortie standard"
(cest--dire la fentre de la console) en appelant System.out.println. Bizarrement, avant le JDK
5.0, il nexistait aucune mthode commode pour lire des entres depuis la fentre de la console.
Heureusement, cette situation vient dtre rectifie.
La lecture dune entre au clavier se fait en construisant un Scanner attach sur lunit "dentre
standard" System.in.
Scanner in = new Scanner(System.in);

Les diverses mthodes de la classe Scanner permettent ensuite de lire les entres. Par exemple, la
mthode nextLine lit une ligne saisie :
System.out.print("What is your name?");
String name = in.nextLine();

Livre Java .book Page 71 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

71

Ici, nous utilisons la mthode nextLine car la saisie pourrait contenir des espaces. Pour lire un seul
mot (dlimit par des espaces), appelez
String firstName = in.next();

Pour lire un entier, utilisez la mthode nextInt:


System.out.print("How old are you? ");
int age = in.nextInt();

De mme, la mthode nextDouble lit le prochain chiffre virgule flottante.


Le programme de lExemple 3.2 demande le nom de lutilisateur et son ge, puis affiche un message
du style
Hello, Cay. Next year, youll be 46

Enfin, ajoutez la ligne


import java.util.*;

au dbut du programme. La classe Scanner est dfinie dans le package java.util. Ds que vous
utilisez une classe qui nest pas dfinie dans le package de base java.lang, vous devez utiliser une
directive import. Nous tudierons les packages et les directives import plus en dtail au Chapitre 4.
Exemple 3.2 : InputTest.java
import java.util.*;
public class InputTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
// rcuprer la premire entre
System.out.print("What is your name? ");
String name = in.nextLine();
// rcuprer la seconde entre
System.out.print("How old are you? ");
int age = in nextInt();
// afficher la sortie la console
System.out.println("Hello, "+name +
". Next year, youll be "+ (age+1));
}
}

INFO
Si vous nutilisez pas le JDK 5.0 ou version suprieure, votre travail sera un peu plus difficile. La mthode la plus
simple consiste utiliser une bote de dialogue de saisie (voir Figure 3.6) :
String input = JOptionPane.showInputDialog(promptString);

La valeur de retour est la chane tape par lutilisateur.


Voici, par exemple, comment demander le nom de lutilisateur :
String input = JOptionPane.showInputDialog("What is your name?");

Livre Java .book Page 72 Jeudi, 25. novembre 2004 3:04 15

72

Au cur de Java 2 - Notions fondamentales

La lecture dun nombre demande un travail supplmentaire. La mthode JOptionPane.showInputDialog


renvoie une chane au lieu dun nombre. La chane est convertie en valeur numrique laide de la mthode
Integer.parseInt ou Double.parseDouble. Par exemple :
String input = JOptionPane.showInputDialog("How old are you?");
int age = Integer.parseInt(input);

Si lutilisateur tape 45, la variable chane input prend la valeur de la chane "45". La mthode Integer.parseInt
convertit la chane en sa valeur numrique, le nombre 45.
La classe JOptionPane est dfinie dans le package javax.swing, vous devez donc ajouter linstruction
import javax.swing.*;

Enfin, chaque fois que votre programme appelle JOptionPane.showInputDialog, vous devez le terminer par
un appel System.exit(0). La raison est un peu technique. Laffichage dune bote de dialogue dmarre un
nouveau thread de contrle. Lors de la sortie de la mthode main, le nouveau thread ne se termine pas automatiquement. Pour terminer tous les threads, vous devez appeler la mthode System.exit (pour plus dinformations
sur les threads, consultez le Chapitre 1 de Au cur de Java 2 Volume 2). Le programme suivant est lquivalent de
lExemple 3.2 avant le JDK 5.0 :
import javax.swing.*;
public class InputTest
{
public static void main(String[] args)
{
String name = JOptionPane.showInputDialog
("What is your name?");
String input = JOptionPane.showInputDialog
("How old are you?");
int age = Integer.parseInt(input);
System.out.println("Hello, "+name +
". Next year, youll be "+ (age+1));
System.exit(0);
}
}

Figure 3.6
Une bote de dialogue
de saisie.

java.util.Scanner 5.0

Scanner(InputStream in)

Construit un objet Scanner partir du flux de saisie donn.

String nextLine()

Lit la prochaine ligne saisie.

String next()

Lit le prochain mot saisi (dlimit par une espace).

int nextInt()
double nextDouble()

Livre Java .book Page 73 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

73

Lisent et transforment la prochaine suite de caractres qui reprsente un entier ou un nombre


virgule flottante.

boolean hasNext()

Teste sil y a un autre mot dans la saisie.

boolean hasNextInt()
boolean hasNextDouble()

Testent si la prochaine suite de caractres reprsente un entier ou un nombre virgule flottante.


javax.swing.JOptionPane 1.2
static String showInputDialog(Objectmessage)

Affiche une bote de dialogue avec un message dinvite, un champ de saisie et les boutons "OK"
et "Cancel" (Annuler). La mthode renvoie la chane entre par lutilisateur.
java.lang.System 1.0
static void exit(intstatus)

Arrte la machine virtuelle et passe le code de status au systme dexploitation. Par convention, un code non zro signale une erreur.

Mise en forme de laffichage


Linstruction System.out.print(x) permet dafficher un nombre x la console. Cette instruction
affichera x avec le maximum de chiffres diffrents de zro (pour le type donn). Par exemple,
double x = 10000.0 / 3.0;
System.out.print(x);

affiche
3333.3333333333335

Cela pose un problme si vous dsirez afficher, par exemple, des euros et des centimes.
Avant le JDK 5.0, laffichage des nombres posait quelques problmes. Heureusement, cette nouvelle
version a rapatri la vnrable mthode printf de la bibliothque C. Par exemple, lappel
System.out.printf("%8.2f", x);

affiche x avec une largeur de champ de 8 caractres et une prcision de 2 caractres. En fait, laffichage contient une espace pralable et les sept caractres
3333.33

Vous pouvez fournir plusieurs paramtres printf, et notamment


System.out.printf("Hello, %s. Next year, youll be %d", name, age);

Chacun des spcificateurs de format qui commencent par le caractre % est remplac par largument
correspondant. Le caractre de conversion qui termine un spcificateur de format indique le type de

Livre Java .book Page 74 Jeudi, 25. novembre 2004 3:04 15

74

Au cur de Java 2 - Notions fondamentales

la valeur mettre en forme : f est un nombre virgule flottante, s une chane et d une valeur dcimale.
Le Tableau 3.5 montre tous les caractres de conversion.
Tableau 3.5 : Conversions pour printf

Caractre de
conversion

Type

Exemple

Entier dcimal

159

Entier hexadcimal

9f

Entier octal

237

Virgule fixe, virgule flottante

15.9

Virgule flottante exponentielle

1.59e+01

Virgule flottante gnrale (le plus court entre e et f)

Virgule flottante hexadcimale

0x1.fccdp3

Chane

Hello

Caractre

Valeur boolenne

true

Code de hachage

42628b2

tx

Date et heure

Voir Tableau 3.7

Symbole du pourcentage

Sparateur de ligne fonction de la plate-forme

Vous pouvez galement spcifier des drapeaux qui contrleront lapparence du rsultat mis en
forme. Le Tableau 3.6 numre les drapeaux. Par exemple, le drapeau virgule ajoute des sparateurs
de groupe. Ainsi,
System.out.printf("%,.2f", 1000.0 / 3.0);

affiche
3,333.33

Vous pouvez afficher plusieurs drapeaux, par exemple "%,(.2f", pour utiliser des sparateurs de
groupe et inclure les nombres ngatifs entre parenthses.
INFO
Vous pouvez utiliser la conversion s pour mettre en forme des objets arbitraires. Lorsquun objet arbitraire implmente linterface Formattable, la mthode formatTo de lobjet est appele. Dans le cas contraire, cest la
mthode toString qui est appele pour transformer lobjet en chane. Nous traiterons de la mthode toString
au Chapitre 5 et des interfaces au Chapitre 6.

Livre Java .book Page 75 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

75

Tableau 3.6 : Drapeaux pour printf

Drapeau

Rle

Exemple

Affiche le signe des nombres positifs et ngatifs.

+3333.33

espace

Ajoute une espace avant les nombres positifs.

| 3333.33|

Ajoute des zros pralables.

003333.33

Justifie le champ gauche.

|3333.33|

Entoure le nombre ngatif de parenthses.

(3333.33)

Ajoute des sparateurs de groupe.

3,333.33

# (pour format f)

Inclut toujours une dcimale.

3,333

# (pour format x ou o)

Ajoute le prfixe 0x ou 0.

0xcafe

Transforme en majuscules.

0XCAFE

Indique lindice de largument mettre en forme ; par


exemple, %1$d %1$x affiche le premier argument en dcimal et hexadcimal.

159 9F

<

Met en forme la mme valeur que la spcification prcdente ; par exemple, %d %<x affiche le mme nombre en
dcimal et hexadcimal.

159 9F

Vous pouvez utiliser la mthode statique String.format pour crer une chane mise en forme sans
lafficher :
String message = String.format("Hello, %s. Next year, youll be %d", name, age);

Mme si nous ne dcrirons pas le type Date en dtail avant le Chapitre 4, pour tre complets, voyons
brivement les options de mise en forme de la date et de lheure de la mthode printf. On utilise un
format deux lettres commenant par t et se terminant par lune des lettres du Tableau 3.7. Par exemple,
System.out.printf("%tc", new Date());

affiche la date et lheure courantes au format


Mon Feb 09 18:05:19 PST 2004

Tableau 3.7 : Caractres de conversion de la date et de lheure

Caractre de
conversion

Type

Exemple

Date et heure compltes

Mon Feb 09
18:05:19 PST 2004

Date au format ISO 8601

2004-02-09

Date au format amricain (mois/jour/anne)

02/09/2004

Heure sur 24 heures

18:05:19

Heure sur 12 heures

06:05:19 pm

Heure sur 24 heures sans secondes

18:05

Livre Java .book Page 76 Jeudi, 25. novembre 2004 3:04 15

76

Au cur de Java 2 - Notions fondamentales

Tableau 3.7 : Caractres de conversion de la date et de lheure (suite)

Caractre de
conversion

Type

Exemple

Anne sur quatre chiffres (avec zros pralables, le cas


chant)

2004

Deux derniers chiffres de lanne (avec zro pralable,


le cas chant)

04

Deux premiers chiffres de lanne (avec zro pralable,


le cas chant)

20

Nom complet du mois

February

b ou h

Nom du mois abrg

Feb

Mois sur deux chiffres (avec zro pralable, le cas chant)

02

Jour sur deux chiffres (avec zro pralable, le cas chant)

09

Jour sur deux chiffres (avec zro pralable, le cas chant)

Jour de la semaine complet

Monday

Jour de la semaine abrg

Mon

Jour de lanne sur trois chiffres (avec zros pralables,


le cas chant), entre 001 et 366

069

Heure sur deux chiffres (avec zro pralable, le cas


chant), entre 00 et 23

18

Heure sur deux chiffres (sans zro pralable), entre 0 et 23

18

Heure sur deux chiffres (avec zro pralable, le cas


chant), entre 01 et 12

06

Heure sur deux chiffres (sans zro pralable), entre 1 et 12

Minutes sur deux chiffres (avec zro pralable, le cas chant)

05

Secondes sur deux chiffres (avec zro pralable, le cas


chant)

19

Millimes de seconde, sur trois chiffres (avec zros


pralables, le cas chant)

047

Nanosecondes sur neuf chiffres (avec zros pralables,


le cas chant)

047000000

Indicateur du matin ou de laprs-midi en majuscules

PM

Indicateur du matin ou de laprs-midi en minuscules

pm

Dcalage numrique RFC 822 du GMT

-0800

Fuseau horaire

PST

Secondes depuis le 1970-01-01 00:00:00 GMT

1078884319

Millimes de seconde depuis le 1970-01-01 00:00:00 GMT

1078884319047

Livre Java .book Page 77 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

77

Comme vous pouvez le voir au Tableau 3.7, certains des formats produisent une partie seulement
dune date donne, par exemple le jour ou le mois. Il serait pourtant assez stupide de fournir la date
plusieurs fois pour la mettre totalement en forme. Cest pourquoi une chane peut indiquer lindice
de largument mettre en forme. Lindice doit suivre immdiatement le % et se terminer par un $.
Par exemple,
System.out.printf("%1$s %2$tB %2$te, %2$tY", "Due date:", new Date());

affiche
Due date: February 9, 2004

Vous pouvez aussi utiliser le drapeau <. Il indique quil faut utiliser largument avec le format qui
prcde. Ainsi, linstruction
System.out.printf("%s %tB %<te, %<tY", "Due date:", new Date());

produit le mme rsultat que linstruction prcdente.


ATTENTION
Les valeurs dindice dargument commencent 1, et non 0 : %1$... met en forme le premier argument. Ceci vite
la confusion avec le drapeau 0.

Vous avez maintenant vu toutes les fonctionnalits de la mthode printf. La Figure 3.7 prsente un
diagramme syntaxique des spcificateurs de mise en forme.
format-specifier:
conversion
character

%
argument
index

flag

width

precision

conversion
character

Figure 3.7
Syntaxe du spcificateur de mise en forme.

INFO
Plusieurs rgles de mise en forme sont spcifiques aux paramtres rgionaux. En France, par exemple, le sparateur
des dizaines est un point, et non une virgule, et Monday devient lundi. Vous verrez au Volume 2 comment modifier
le comportement de vos applications en fonction des pays.

ASTUCE
Si vous utilisez une version de Java pralable au JDK 5.0, utilisez les classes NumberFormat et DateFormat au lieu
de printf.

Livre Java .book Page 78 Jeudi, 25. novembre 2004 3:04 15

78

Au cur de Java 2 - Notions fondamentales

Flux dexcution
Comme tout langage de programmation, Java gre les instructions conditionnelles et les boucles afin de
contrler le flux dexcution (ou flux de contrle). Nous commencerons par tudier les instructions
conditionnelles avant de passer aux boucles. Nous terminerons par linstruction switch, qui peut tre
employe lorsque vous devez tester les nombreuses valeurs possibles dune mme expression.
INFO C++
Les constructions du flux dexcution de Java sont comparables celles de C et de C++, deux exceptions prs.
Il nexiste pas dinstruction goto, mais une version "tiquete" de break peut tre employe pour sortir dune
boucle imbrique (l o vous auriez peut-tre employ goto dans un programme C). Enfin, le JDK 5.0 ajoute une
variante la boucle for qui na pas son pareil en C ou C++. Elle est identique la boucle foreach du C#.

Porte dun bloc


Avant dexaminer les structures de contrle, vous devez savoir ce quest un bloc.
Un bloc, ou instruction compose, est un groupe dinstructions simples dlimit par une paire
daccolades. Les blocs dterminent la porte des variables. Ils peuvent tre imbriqus lintrieur
dun autre bloc. Voici un bloc imbriqu dans le bloc de la mthode main:
public static void main(String[] args)
{
int n;
. . .
{
int k;
. . .
} // k nest dfini que jusquici
}

Prcisons quil nest pas possible de dclarer des variables homonymes dans deux blocs imbriqus.
Dans lexemple suivant, la seconde dclaration de n est une erreur et le programme ne peut pas tre
compil :
public static void main(String[] args)
{
int n;
. . .
{
int k;
int n; // erreur--impossible de redfinir n dans un bloc imbriqu
. . .
}
}

INFO C++
En C++, il est possible de redfinir une variable lintrieur dun bloc imbriqu. La dfinition la plus interne cache
alors la dfinition externe. Cela constitue une source derreur, et Java ne lautorise pas.

Livre Java .book Page 79 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

79

Instructions conditionnelles
Linstruction conditionnelle en Java prend la forme :
if (condition) instruction

La condition doit tre incluse entre parenthses. En Java, comme dans la plupart des langages de
programmation, vous souhaiterez souvent excuter plusieurs instructions lorsquune condition est
vraie. Dans ce cas, vous utiliserez un bloc dinstructions qui prend la forme :
{
instruction1
instruction2
. . .
}

Par exemple :
if (yourSales >= target)
{
performance = "Satisfactory";
bonus = 100;
}

Dans cet extrait de code, toutes les instructions qui se trouvent lintrieur des accolades seront
excutes lorsque la valeur de yourSales sera suprieure la valeur de target (voir Figure 3.8).
Figure 3.8
Organigramme
de linstruction if.
NO
yourSales target

YES

performance
= Satisfactory

bonus=100

INFO
Un bloc (parfois appel instruction compose) permet de regrouper plus dune instruction (simple) dans une structure de programmation Java qui, sans ce regroupement, ne pourrait contenir quune seule instruction (simple).

Livre Java .book Page 80 Jeudi, 25. novembre 2004 3:04 15

80

Au cur de Java 2 - Notions fondamentales

Linstruction conditionnelle de Java a laspect suivant (voir Figure 3.9) :


Figure 3.9
Organigramme
de linstruction if/else.
YES

if (condition)

yourSales target

NO

performance
=Satisfactory

performance
=Unsatisfactory

bonus=
100+0.01*
(yourSalestarget)

bonus=0

instruction1

else

instruction2;

Par exemple :
if (yourSales >= target)
{
performance = "Satisfactory";
bonus = 100+0.01 * (yourSales - target);
}
else
{
performance = "Unsatisfactory";
bonus = 0;
}

La partie else est toujours facultative. Une directive else est toujours associe linstruction if la
plus proche. Par consquent, dans linstruction
if (x <= 0) if (x == 0) sign = 0; else sign = -1;

la directive else appartient au second if.


Des squences rptes if . . . else if . . . sont trs frquentes (voir Figure 3.10). Par exemple :
if (yourSales >= 2 * target)
{
performance = "Excellent";
bonus = 1000;
}
else if (yourSales >= 1.5 * target)

Livre Java .book Page 81 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

{
performance = "Fine";
bonus = 500;
}
else if (yourSales >= target)
{
performance = "Satisfactory";
bonus = 100;
}
else
{
System.out.println("Youre fired");
}

yourSales 2*target

YES

performance

bonus=1000

performance
=Fine

bonus=500

performance
=Satisfactory

bonus=100

NO

yourSales 1.5*target

YES

NO

YES
yourSales target

NO

Print
You're fired

Figure 3.10
Organigramme de linstruction if/else if (branchement multiple).

81

Livre Java .book Page 82 Jeudi, 25. novembre 2004 3:04 15

82

Au cur de Java 2 - Notions fondamentales

Boucles
La boucle while excute une instruction (qui peut tre une instruction de bloc) tant quune condition
est vraie. Sa forme gnrale est la suivante :
while (condition) instruction

La boucle while ne sexcute jamais si la condition est fausse ds le dpart (voir Figure 3.11).
Figure 3.11
Organigramme
de linstruction while.
NO
balance<goal

YES

update
balance

years++

Print years

Dans lExemple 3.3, nous crivons un programme permettant de dterminer combien de temps sera
ncessaire pour conomiser une certaine somme vous permettant de prendre une retraite bien mrite, en
supposant que vous dposiez chaque anne une mme somme dargent un taux dintrt spcifi.
Dans notre exemple, nous incrmentons un compteur et nous mettons jour le total cumul dans le
corps de la boucle jusqu ce que le total excde le montant souhait :
while (balance < goal)
{
balance += payment;

Livre Java .book Page 83 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

83

double interest = balance * interestRate/ 100;


balance += interest;
years++;
}
System.out.println(years + " years.");

Ne vous fiez pas ce programme pour prvoir votre retraite. Nous avons laiss de ct quelques
dtails comme linflation et votre esprance de vie.
Le test dune boucle while est effectu avant lexcution du corps de la boucle. Par consquent, ce
bloc peut ne jamais tre excut. Si vous voulez tre certain que le bloc soit excut au moins une
fois, vous devez placer la condition de test en fin de boucle. Pour cela, employez une boucle do/while,
dont voici la syntaxe :
do instruction while (condition);

Cette instruction excute le bloc avant de tester la condition. Si celle-ci est fausse, le programme
rexcute le bloc avant deffectuer un nouveau test, et ainsi de suite. Par exemple, le code de lExemple 3.4 calcule le nouveau solde de votre compte retraite, puis vous demande si vous tes prt partir
la retraite :
do
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
year++;
// afficher le solde actuel
. . .
// demander si prt prendre la retraite
// et rcuprer la rponse
. . .
}
while (input.equals("N"));

Tant que la rponse de lutilisateur est "N", la boucle est rpte (voir Figure 3.12). Ce programme
est un bon exemple dune boucle devant tre excute au moins une fois, car lutilisateur doit
pouvoir vrifier le solde avant de dcider sil est suffisant pour assurer sa retraite.
Exemple 3.3 : Retirement.java
import java.util.*;
public class Retirement
{
public static void main(String[] args)
{
// lire les infos entres
Scanner input = new Scanner(System.in);
System.out.print("How much do you need to retire?");
double goal = in.nextDouble();
System.out.print("How much money will you contribute every year?");
double payment = in.nextDouble();
System.out.print("Interst rate in %:");
double interestRate = in.nextDouble();

Livre Java .book Page 84 Jeudi, 25. novembre 2004 3:04 15

84

Au cur de Java 2 - Notions fondamentales

double balance = 0;
int years = 0;
// mettre jour le solde du compte tant que cible non atteinte
while (balance < goal)
{
// ajouter versements et intrts de cette anne
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
years++;
}
System.out.println
("You can retire in "+years+" years.");
}
}

Exemple 3.4 : Retirement2.java


import java.util.*;
public class Retirement2
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("How much money will you contribute every year?");
double payment = in.nextDouble();
System.out.print("Interest rate in %:");
double interestRate = in.nextDouble();
double balance = 0;
int year = 0;
String input;
// mettre jour le solde du compte tant que lutilisateur
// nest pas prt prendre sa retraite
do
{
// ajouter versements et intrts de cette anne
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
year++;
// afficher le solde actuel
System.out.println("After year %d, your balance is %,.2f%n",
year, balance);
// demander si prt pour la retraite
System.out.print("Ready to retire? (Y/N)");
input = in.next();
}

Livre Java .book Page 85 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

85

while (input.equals("N"));
}
}

Figure 3.12
Organigramme
de linstruction do/while.
update
balance

print balance
ask Ready
to retire?
(Y/N)

read
input

YES
input=N

NO

Boucles dtermines
La boucle for est une construction trs gnrale pour grer une itration contrle par un compteur
ou une variable similaire, mis jour aprs chaque itration. Comme le montre la Figure 3.13, le code
suivant affiche les nombres 1 10 sur lcran :
for (int i = 1; i <= 10; i++)
System.out.println(i);

Le premier lment de linstruction for contient gnralement linitialisation du compteur. Le


deuxime lment fournit la condition de test qui sera vrifie avant chaque passage dans la boucle ;
le troisime indique comment le compteur doit tre mis jour.
Bien que Java, comme C++, autorise pratiquement nimporte quelle expression dans les trois
lments dune boucle for, une convention tacite fait que ces lments doivent respectivement se

Livre Java .book Page 86 Jeudi, 25. novembre 2004 3:04 15

86

Au cur de Java 2 - Notions fondamentales

contenter dinitialiser, de tester et de mettre jour la mme variable compteur. Il est possible dcrire
des boucles trs absconses si lon ne respecte pas cette convention.
Figure 3.13
Organigramme
de linstruction for.

i=1

10

NO

YES

Print i

i++

Mme en suivant cette rgle, de nombreuses possibilits sont offertes. Il est ainsi possible de crer
des boucles dcrmentales :
for (int i = 10; i > 0; i--)
System.out.println("Counting down . . . "+i);
System.out.println("Blastoff!");

ATTENTION
Soyez prudent lorsque vous testez lgalit de deux nombres rels. Une boucle for comme celle-ci :
for (double x = 0; x!= 10; x += 0.1) . . .

risque de ne jamais se terminer. Du fait des erreurs darrondi, la valeur finale risque de ntre jamais atteinte. Par
exemple, dans la boucle ci-dessus, x saute de 9.99999999999998 10.09999999999998, puisquil nexiste pas de
reprsentation binaire exacte pour 0.1.

Livre Java .book Page 87 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

87

Lorsque vous dclarez une variable dans le premier lment dune instruction for, la porte de cette
variable stend jusqu la fin du corps de la boucle :
for (int i = 1; i <= 10; i++)
{
. . .
}
// i nest plus dfini ici

En particulier, si vous dfinissez une variable lintrieur dune instruction for, vous ne pouvez pas
utiliser cette variable en dehors de la boucle. En consquence, si vous dsirez utiliser la valeur finale du
compteur en dehors dune boucle for, la variable compteur doit tre dclare en dehors de cette boucle !
int i;
for (i = 1; i <= 10; i++)
{
. . .
}
// i est toujours dfini ici

En revanche, vous pouvez dfinir des variables de mme nom dans des boucles for spares :
for (int i = 1; i <= 10; i++)
{
. . .
}
. . .
for (int i = 11; i <= 20; i++) // ok pour redfinir i
{
. . .
}

Bien entendu, une boucle for quivaut une boucle while. Pour tre plus prcis,
for (int i = 10; i > 0; i--)
System.out.println("Counting down . . . " + i);

quivaut exactement :
int i = 10;
while (i > 0)
{
System.out.println("Counting down . . . " + i);
i--;
}

LExemple 3.5 montre un exemple typique de boucle for.


Le programme calcule vos chances de gagner une loterie. Si vous devez, par exemple, trouver 6 des
nombres de 1 50 pour gagner, il y a

( 50 49 48 47 46 45 )
-----------------------------------------------------------------------(1 2 3 4 5 6)
tirages possibles, et vous avez une chance sur 15 890 700. Bonne chance !
En gnral, si vous choisissez k nombres parmi n, il y a

n ( n 1 ) ( n 1 ) ... ( n k + 1 )
-------------------------------------------------------------------------------------------1 2 3 ... k
tirages possibles. La boucle for qui suit calcule cette valeur :
int lotteryOdds = 1;
for (int i = 1; i <= k; i++)
lotteryOdds = lotteryOdds * (n - i+1) / i;

Livre Java .book Page 88 Jeudi, 25. novembre 2004 3:04 15

88

Au cur de Java 2 - Notions fondamentales

INFO
Voir un peu plus loin pour obtenir une description de la boucle for gnralise (aussi appele boucle "for each")
ajoute au JDK 5.0.

Exemple 3.5 : LotteryOdds.java


import java.swing.*;
public class LotteryOdds
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("How many numbers do you need to draw? ");
int k = in.nextInt();
System.out.print("What is the highest number you can draw? ");
int n = in.nextInt();
/*
calculer le binme
n * (n - 1) * (n - 2) * . . . * (n - k+1)
------------------------------------------1 * 2 * 3 * . . . * k
*/
int lotteryOdds = 1;
for (int i = 1; i <= k; i++)
lotteryOdds = lotteryOdds * (n - i+1) / i;
System.out.println
("Your odds are 1 in "+lotteryOdds+". Good luck!");
}
}

Slections multiples linstruction switch


La construction if/else peut se rvler assez lourde quand vous devez traiter plusieurs slections et
de multiples alternatives. Java dispose de linstruction switch qui reproduit exactement celle de C et
C++, y compris ses dfauts.
Par exemple, si vous crez un systme de menu ayant quatre alternatives, comme celui de la
Figure 3.14, vous pouvez utiliser un code comparable celui-ci :
Scanner in = new Scanner(System.in);
System.out.print("Select an option (1, 2, 3, 4)");
int choice = in.nextInt();
switch (choice)
{
case 1:
. . .
break;
case 2:
. . .
break;
case 3:
. . .
break;
case 4:
. . .
break;
default:

Livre Java .book Page 89 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

// entre incorrecte
. . .
break;
}

Figure 3.14
Organigramme
de linstruction switch.

choice = 1

YES

. . .

YES

. . .

YES

. . .

YES

. . .

NO

choice = 2

NO

choice = 3

NO

choice = 4

NO
(default)
bad input

89

Livre Java .book Page 90 Jeudi, 25. novembre 2004 3:04 15

90

Au cur de Java 2 - Notions fondamentales

Lexcution commence ltiquette case dont la valeur correspond la valeur de slection, puis elle
se poursuit jusqu une instruction break ou jusqu la fin du bloc switch. Si aucune correspondance nest trouve, la clause default est excute, si elle existe.
Notez que les valeurs de case doivent tre des entiers ou des constantes numres. Il nest pas
possible de tester des chanes. Par exemple, le code suivant est erron :
String input = . . .;
switch (input) // ERREUR
{
case "A": // ERREUR
. . .
break;
. . .
}

ATTENTION
Il est possible dexcuter plusieurs instructions case. Si vous oubliez dajouter la clause break la fin dune instruction case, lexcution se poursuit avec le bloc case qui suit ! Ce comportement est trs dangereux et est une cause
commune derreur. Cest pourquoi nous nutilisons pas linstruction switch dans nos programmes.

Interrompre le flux dexcution


Bien que les concepteurs de Java aient conserv goto en tant que mot rserv, ils ne lont pas inclus
dans le langage. En gnral, lemploi dinstructions goto est considr comme inlgant et maladroit. Certains programmeurs pensent nanmoins que les forces anti-goto sont alles trop loin (un
fameux article de Donald Knuth sintitule "La programmation structure avec des goto"). Ils avancent que si lutilisation frquente de goto est dangereuse, quitter immdiatement une boucle peut
parfois tre utile. Les concepteurs de Java ont admis cette thse et ont mme ajout une nouvelle
instruction pour ce mcanisme : linterruption "tiquete" (labeled break).
Observons dabord linstruction break normale. La mme instruction qui est employe pour sortir
dun bloc switch permet de quitter une boucle. Par exemple :
while (years <= 100)
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
if (balance >= goal) break;
years++;
}

Lexcution de programme quitte la boucle si years > 100 au dbut de la boucle, ou si balance
>= goal au milieu de la boucle. Bien entendu, vous auriez pu calculer la mme valeur pour years
sans ajouter break, de la faon suivante :
while (years <= 100 && balance < goal)
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
if (balance < goal)
years++;
}

Livre Java .book Page 91 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

91

Notez toutefois que le test balance < goal est reproduit deux fois dans cette version. Pour viter
cette redondance, certains programmeurs prfrent linstruction break.
Contrairement C++, Java propose galement une instruction dinterruption tiquete permettant de
quitter des boucles imbriques. Il arrive quun vnement particulier survienne au sein dune boucle
profondment imbrique. Dans ce cas, il est souhaitable de quitter lensemble des boucles, et pas
seulement celle qui a vu surgir cet vnement. Il ne serait pas simple de programmer cette situation
en ajoutant des conditions supplmentaires aux diverses boucles.
Nous allons prsenter un exemple qui montre le fonctionnement de ce mcanisme. Remarquez que
ltiquette doit prcder la plus externe des boucles que vous souhaitez quitter. Elle doit tre suivie
de deux-points (:) :
Scanner in = new Scanner(System.in);
int n;
read_data:
while (. . .) // cette instruction de boucle est tiquete
{
. . .
for (. . .) // cette boucle interne nest pas tiquete
{
System.out.print("Enter a number >= 0");
n = in.nextInt();
if (n < 0) // Ne doit pas se produire, impossible de continuer
break read_data;
// sortir de la boucle de lecture
. . .
}
}
// cette instruction est excute immdiatement aprs break
if (n < 0) // vrifier si situation anormale
{
// traiter situation anormale
}
else
{
// poursuivre le traitement normal
}

Si lentre est invalide, linstruction break tiquete saute aprs le bloc tiquet. Comme avec toute
utilisation de break, il vous faut alors effectuer un test pour savoir si la boucle sest termine normalement ou si elle a t interrompue.
INFO
Curieusement, vous pouvez attribuer une tiquette nimporte quelle instruction, y compris une instruction if ou
un bloc dinstructions. Par exemple :
tiquette:
{
. . .
if (condition) break tiquette; // sortie du bloc
. . .
}
// saut ici lors de lexcution de linstruction break

Livre Java .book Page 92 Jeudi, 25. novembre 2004 3:04 15

92

Au cur de Java 2 - Notions fondamentales

Si linstruction goto vous manque vraiment, et que vous puissiez placer un bloc qui se termine juste avant lendroit
o vous voulez sauter, une instruction break fera laffaire ! Cette approche nest bien entendu pas conseille. Notez
aussi que vous ne pouvez sauter quen dehors dun bloc, jamais dans un bloc.

Il existe enfin une instruction continue qui, comme linstruction break, interrompt le flux normal
dexcution. Linstruction continue transfre le contrle en tte de la boucle englobante la plus
interne. En voici un exemple :
Scanner in = new Scanner(System.in);
while (sum < goal)
{
System.out.print("Enter a number: ");
n = in.nextInt();
if (n < 0) continue;
sum += n; // nest pas excut si n < 0
}

Si n < 0, linstruction continue saute immdiatement en tte de boucle, et nexcute pas le reste de
litration en cours.
Si linstruction continue est employe dans une boucle for, elle provoque un saut vers la partie
"mise jour" de la boucle for. Par exemple :
for (count = 1; count < 100; count++)
{
System.out.print("Enter a number, -1 to quit: ");
n = in.nextInt();
if (n < 0) continue;
sum += n; // nest pas excut si n < 0
}

Si n < 0, linstruction continue provoque un saut vers linstruction count++.


Il existe aussi une forme tiquete de linstruction continue qui provoque un saut vers len-tte de
la boucle portant ltiquette correspondante.
ASTUCE
De nombreux programmeurs trouvent les instructions break et continue peu claires. Ces instructions sont absolument facultatives ; vous pouvez toujours exprimer la mme logique sans y avoir recours. Dans cet ouvrage, nous
nutilisons jamais ces instructions.

Grands nombres
Si la prcision des types de base entier et flottant nest pas suffisante, vous pouvez avoir recours des
classes trs utiles du package java.math, appeles BigInteger et BigDecimal. Ces classes permettent de manipuler des nombres comprenant une longue squence arbitraire de chiffres. La classe
BigInteger implmente une arithmtique de prcision arbitraire pour les entiers, et BigDecimal
fait la mme chose pour les nombres virgule flottante.
Utilisez la mthode statique valueOf pour transformer un nombre ordinaire en grand nombre :
BigInteger a = BigInteger.valueOf(100);

Livre Java .book Page 93 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

93

Il nest malheureusement pas possible dutiliser les oprateurs mathmatiques habituels tels que + et
* pour combiner des grands nombres. Vous devez, la place, avoir recours des mthodes telles que
add et multiply dans les classes des grands nombres :
BigInteger c = a.add(b); // c = a+b
BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); // d = c * (b+2)

INFO C++
Contrairement C++, Java ne prvoit pas de surcharge programmable des oprateurs. Il nest pas possible au
programmeur de la classe BigInteger de redfinir les oprateurs + et * pour leur attribuer les oprations add et
multiply des classes BigInteger. Les concepteurs ont en fait surcharg loprateur + pour indiquer la concatnation de chanes, mais ils ont choisi de ne pas surcharger les autres oprateurs, sans donner au programmeur la possibilit de le faire lui-mme.

LExemple 3.6 montre le programme lotteryOdds de lExemple 3.5 modifi, afin de fonctionner
avec les grands nombres. Par exemple, si vous tes invit participer une loterie pour laquelle vous
devez choisir 60 nombres parmi 490 possibles, ce programme vous dira que vous avez une chance sur
716395843461995557415116222540092933411717612789263493493351013459481104668848.
Bonne chance !
Le programme de lExemple 3.5 comprenait linstruction de calcul suivante :
lotteryOdds = lotteryOdds * (n - i+1) / i;

Avec lutilisation des grands nombres, linstruction quivalente devient :


lotteryOdds = lotteryOdds.multiply(BigInteger.valueOf(n - i+1))
.divide(BigInteger.valueOf(i));

Exemple 3.6 : BigIntegerTest.java


import java.math.*;
import java.util.*;
public class BigIntegerTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("How many numbers do you need to draw? ");
int k = in.nextInt();
System.out.print("What is the highest number you can draw? ");
int n = in.nextInt();
/*
Calculer le binme
n * (n - 1) * (n - 2) * . . . * (n - k+1)
------------------------------------------1 * 2 * 3 * . . . * k
*/
BigInteger lotteryOdds = BigInteger.valueOf(1);

Livre Java .book Page 94 Jeudi, 25. novembre 2004 3:04 15

94

Au cur de Java 2 - Notions fondamentales

for (int i = 1; i <= k; i++)


lotteryOdds = lotteryOdds
.multiply(BigInteger.valueOf(n - i+1))
.divide(BigInteger.valueOf(i));
System.out.println("Your odds are 1 in "+lotteryOdds +
". Good luck!");
}
}
java.math.BigInteger 1.1

BigInteger add(BigInteger other)


BigInteger subtract(BigInteger other)
BigInteger multiply(BigInteger other)
BigInteger divide(BigInteger other)
BigInteger mod(BigInteger other)

Renvoient respectivement la somme, la diffrence, le produit, le quotient et le reste de BigInteger


et de other.

int compareTo(BigInteger other)

Renvoie 0 si BigInteger est gal other, un rsultat ngatif sil est infrieur other et un
rsultat positif sinon.

static BigInteger valueOf(long x)

Renvoie un grand entier dont la valeur est gale x.


java.math.BigDecimal 1.1

BigDecimal add(BigDecimal other)


BigDecimal subtract(BigDecimal other)
BigDecimal multiply(BigDecimal other)
BigDecimal divide(BigDecimal other, roundingMode mode) 5.0

Renvoient respectivement la somme, la diffrence, le produit ou le quotient de BigDecimal et de


other. Pour calculer le quotient, vous devez fournir un mode darrondi. Le mode RoundingMode.HALF_UP est celui que vous avez tudi lcole (cest--dire arrondi au chiffre infrieur si
0 4, arrondi au chiffre suprieur si 5 9). Cela convient pour les calculs de routine. Consultez
la documentation API en ce qui concerne les autres modes darrondi.

int compareTo(BigDecimal other)

Renvoie 0 si BigDecimal est gal other, un rsultat ngatif sil est infrieur other et un
rsultat positif sinon.

static BigDecimal valueOf(long x)


static BigDecimal valueOf(long x, int scale)

Renvoient un grand dcimal dont la valeur est gale x or x/10scale.

Tableaux
Un tableau est une structure de donnes qui stocke une srie de valeurs du mme type. Vous accdez
chaque valeur individuellement laide dun entier indice. Par exemple, si a est un tableau
dentiers, a[i] est le ime entier du tableau.

Livre Java .book Page 95 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

95

Vous dclarez une variable tableau en spcifiant son type qui est le type dlment suivi de []
et le nom de la variable tableau. Voici, par exemple, la dclaration dun tableau a dentiers :
int[] a;

Cette instruction ne dclare toutefois que la variable a. Elle ninitialise pas a comme un tableau.
Loprateur new cre le tableau
int[] a = new int[100];

Cette instruction cre un tableau qui peut stocker 100 entiers.


INFO
Vous pouvez dfinir une variable de tableau soit sous la forme
int[] a;

soit sous la forme


int a[];

La plupart des programmeurs Java prfrent le premier style car il spare nettement le type int[] (tableau
dentiers) du nom de la variable.

Les lments du tableau sont numrots de 0 99 (et non de 1 100). Une fois que le tableau est
cr, vous pouvez remplir ses lments, par exemple laide dune boucle :
int[] a = new int[100];
for (int i = 0; i < 100; i++)
a[i] = i; // remplit le tableau avec les valeurs de 0 99

ATTENTION
Si vous construisez un tableau de 100 lments et que vous essayiez daccder llment a[100] (ou tout autre
indice en dehors de la plage 0 99), votre programme se terminera avec une exception "array index out of bounds"
(indice de tableau hors limites).

Vous pouvez trouver le nombre des lments dun tableau laide de nomTableau.length. Par
exemple,
for (int i = 0; i < a.length; i++)
System.out.println(a[i]);

Une fois un tableau cr, vous ne pouvez pas modifier sa taille (mais vous pouvez changer un
lment individuel du tableau). Si vous devez modifier souvent la taille dun tableau pendant
lexcution dun programme, vous pouvez avoir recours une structure de donnes diffrente appele
liste de tableaux (voir le Chapitre 5 pour plus dinformations ce sujet).

La boucle "for each"


Le JDK 5.0 a introduit une construction de boucle performante qui vous permet de parcourir chaque
lment dun tableau (ainsi que dautres collections dlments) sans avoir vous proccuper des
valeurs dindice.

Livre Java .book Page 96 Jeudi, 25. novembre 2004 3:04 15

96

Au cur de Java 2 - Notions fondamentales

La boucle for amliore


for (variable: collection) instruction

dfinit la variable donne sur chaque lment de la collection, puis excute linstruction (qui, bien
sr, peut tre un bloc). Lexpression collection doit tre un tableau ou un objet dune classe qui
implmente linterface Iterable, comme ArrayList. Nous verrons les listes de tableaux au Chapitre 5 et linterface Iterable au Chapitre 2 du Volume 2.
Par exemple,
for (int element: a)
System.out.println(element);

affiche chaque lment du tableau a sur une ligne spare.


Il est conseill de lire cette boucle sous la forme "pour chaque lment dans a". Les concepteurs du
langage Java ont envisag dutiliser des mots cls comme foreach et in. Mais cette boucle a t
ajoute avec un peu de retard au langage Java et, au final, personne na voulu casser un ancien code
qui contenait dj des mthodes ou des variables avec les mmes noms (comme System.in).
Bien entendu, vous pourriez obtenir le mme effet avec une boucle for traditionnelle :
for (int i = 0; i < a.length; i++)
System.out.println(a[i]);

Toutefois, la boucle "for each" est plus concise et moins sujette erreur (vous navez pas vous
inquiter des valeurs dindice de dbut et de fin, qui sont souvent pnibles).
INFO
La variable loop de la boucle "for each" parcourt les lments dun tableau, et non les valeurs dindice.

La boucle "for each" est une amlioration agrable de la boucle traditionnelle si vous devez traiter
tous les lments dune collection. Il y a toutefois de nombreuses opportunits dutiliser la boucle
for traditionnelle. Vous ne voudrez peut-tre pas, par exemple, parcourir la totalit de la collection
ou pourriez avoir besoin de la valeur dindice lintrieur de la boucle.

Initialiseurs de tableaux et tableaux anonymes


Java propose un raccourci pour crer un objet tableau et linitialiser simultanment. Voici un exemple de
la syntaxe employer :
int[] smallPrimes = { 2, 3, 5, 7, 11, 13 };

Remarquez quil nest pas ncessaire dappeler new lorsque vous utilisez cette syntaxe.
Il est mme possible dinitialiser un tableau anonyme :
new int[] { 17, 19, 23, 29, 31, 37 }

Cette expression alloue un nouveau tableau et le remplit avec les valeurs spcifies entre les accolades. Elle dtermine le nombre de valeurs fournies et affecte au tableau le mme nombre dlments.
Cette syntaxe est employe pour rinitialiser un tableau sans crer une nouvelle variable. Lexemple
smallPrimes = new int[] { 17, 19, 23, 29, 31, 37 };

Livre Java .book Page 97 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

97

est un raccourci pour


int[] anonymous = { 17, 19, 23, 29, 31, 37 };
smallPrimes = anonymous;

INFO
Il est lgal davoir des tableaux de longueur 0. Un tel tableau peut tre utile si vous crivez une mthode qui calcule
un rsultat de tableau et que ce rsultat puisse tre vide. Un tableau de longueur 0 se construit de la faon suivante :
new typeElment[0]

Notez quun tableau de longueur 0 est diffrent de null (voir le Chapitre 4 pour plus dinformations concernant
null).

Copie des tableaux


Il est possible de copier une variable tableau dans une autre, mais les deux variables feront alors
rfrence au mme tableau :
int[] luckyNumbers = smallPrimes;
luckyNumbers[5] = 12; // smallPrimes[5] vaut maintenant 12

La Figure 3.15 montre le rsultat. Si vous voulez effectivement copier toutes les valeurs dun tableau
dans un autre, il faut employer la mthode arraycopy de la classe System. Sa syntaxe est la
suivante :
System.arraycopy(from, fromIndex, to, toIndex, count);

Le tableau to doit disposer de suffisamment despace pour contenir les lments copis.
Figure 3.15
Copie dune
variable tableau.

smallPrimes =
luckyNumbers =

2
3
5
7
11
12

Par exemple, les instructions suivantes crent deux tableaux, puis copient les quatre derniers
lments du premier tableau dans le second tableau. La copie dbute la position 2 du tableau
source ; quatre lments sont copis en partant de la position 3 du tableau cible. Le rsultat est donn
la Figure 3.16 :
int[] smallPrimes = {2, 3, 5, 7, 11, 13};
int[] luckyNumbers = {1001, 1002, 1003, 1004, 1005, 1006, 1007};
System.arraycopy(smallPrimes, 2, luckyNumbers, 3, 4);
for (int i = 0; i < luckyNumbers.length; i++)
System.out.println(i+": "+luckyNumbers[i]);

On obtient en sortie :
0:
1:
2:
3:

1001
1002
1003
5

Livre Java .book Page 98 Jeudi, 25. novembre 2004 3:04 15

98

Au cur de Java 2 - Notions fondamentales

4: 7
5: 11
6: 13

Figure 3.16

smallPrimes =

Copie des valeurs


vers un tableau.

luckyNumbers =

2
3
5
7
11
13

1001
1002
1003
5
7
11
13

INFO C++
Un tableau Java est assez diffrent dun Tableau C/C++ dans la pile (stack). Il peut cependant tre compar un pointeur
sur un tableau allou dans le tas (segment heap de la mmoire). Cest--dire que
int[] a = new int[100]; // en Java

nest pas la mme chose que


int a[100]; // en C++

mais plutt
int* a = new int[100]; // en C++

En Java, loprateur [] est prdfini pour effectuer une vrification de limites. De plus, larithmtique de pointeur
nest pas possible vous ne pouvez pas incrmenter a pour quil pointe sur llment suivant du tableau.

Paramtres de ligne de commande


Vous avez dj vu plusieurs exemples de tableaux Java. Chaque programme Java a une mthode
main avec un paramtre String[] args. Celui-ci indique que la mthode main reoit un tableau de
chanes, qui sont les arguments spcifis sur la ligne de commande.
Examinez, par exemple, ce programme :
public class Message
{
public static void main(String[] args)
{
if (args[0].equals("-h"))
System.out.print("Hello,");
else if (args[0].equals("-g"))
System.out.print("Goodbye,");

Livre Java .book Page 99 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

99

// afficher les autres arguments de ligne de commande


for (int i = 1; i < args.length; i++)
System.out.print(" "+args[i]);
System.out.println("!");
}
}

Si le programme est appel de la faon suivante :


java Message -g cruel world

le tableau args a le contenu suivant :


args[0]: "-g"
args[1]: "cruel"
args[2]: "world"

Le programme affiche le message :


Goodbye, cruel world!

INFO C++
Dans la mthode main dun programme Java, le nom du programme nest pas stock dans le tableau args. Si, par
exemple, vous lancez le programme ainsi :
java Message -h world

partir de la ligne de commande, args[0] vaudra "-h" et non "Message" ou "java".

Tri dun tableau


Si vous voulez trier un tableau de nombres, utilisez une des mthodes sort de la classe Arrays:
int[] a = new int[10000];
. . .
Arrays.sort(a)

Cette mthode utilise une version adapte de lalgorithme QuickSort qui se rvle trs efficace sur la
plupart des ensembles de donnes. La classe Arrays fournit plusieurs autres mthodes de gestion
des tableaux ; vous trouverez leur description dans les notes API situes la fin de cette section.
Le programme de lExemple 3.7 montre le fonctionnement des tableaux. Il choisit une combinaison
alatoire de nombres pour une loterie. Sil sagit dune loterie o il faut choisir 6 nombres sur 49, le
programme peut afficher :
Bet the following combination. Itll make you rich!
4
7
8
19
30
44

Pour slectionner une telle srie alatoire de nombres, il faut dabord remplir un tableau numbers
avec les valeurs 1, 2, . . ., n:
int[] numbers = new int[n];
for (int i = 0; i < numbers.length; i++)
numbers[i] = i+1;

Un second tableau contient les numros tirer :

Livre Java .book Page 100 Jeudi, 25. novembre 2004 3:04 15

100

Au cur de Java 2 - Notions fondamentales

int[] result = new int[k];

Nous tirons maintenant k numros. La mthode Math.random renvoie un nombre alatoire virgule
flottante entre 0 (inclus) et 1 (exclu). En multipliant le rsultat par n, nous obtenons un nombre alatoire entre 0 et n-1:
int r = (int)(Math.random() * n);

Nous dfinissons le ime rsultat comme le numro de cet indice. Au dpart, il sagit simplement de
r lui-mme, mais vous allez voir quen fait, le contenu du tableau numbers change aprs chaque
tirage :
result[i] = numbers[r];

Nous devons maintenant nous assurer que nous ne tirerons pas deux fois le mme numro tous les
numros dun tirage doivent tre diffrents. Nous crasons donc numbers[r] avec le dernier numro
du tableau et dcrmentons n de 1 :
numbers[r] = numbers[n - 1];
n--;

A chaque tirage, nous extrayons en fait un indice, et non la valeur relle. Lindice pointe sur un
tableau qui contient les valeurs qui nont pas encore t tires.
Aprs avoir tir k numros, le tableau result est tri pour que la sortie soit plus parlante :
Arrays.sort(result);
for (int r: result)
System.out.println(r);

Exemple 3.7 : LotteryDrawing.java


import java.util.*;
public class LotteryDrawing
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("How many numbers do you need to draw? ");
int k = in.nextInt();
System.out.print("What is the highest number you can draw? ");
int n = in.nextInt();
// remplir un tableau avec les nombres 12 3 . . . n
int[] numbers = new int[n];
for (int i = 0; i < numbers.length; i++)
numbers[i] = i+1;
// tirer k nombres et les mettre dans un second tableau
int[] result = new int[k];
for (int i = 0; i < result.length; i++)
{
// crer un indice alatoire entre 0 et n - 1
int r = (int)(Math.random() * n);
// choisir llment cet emplacement alatoire
result[i] = numbers[r];

Livre Java .book Page 101 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

101

// dplacer le dernier lment vers lemplac. alatoire


numbers[r] = numbers[n - 1];
n--;
}
// imprimer le tableau tri
Arrays.sort(result);
System.out.println
("Bet the following combination Itll make you rich!");
for (int r: result)
System.out.println(r);
}
}
java.lang.System 1.1
static void arraycopy(Object from, int fromIndex, Object to, int toIndex, int
count)

Copie les lments du premier tableau dans le second.


Paramtres :

from

Un tableau de nimporte quel type (le Chapitre 5 explique


pourquoi il sagit dun paramtre de type Object).

fromIndex

Indice partir duquel des lments seront lus.

to

Tableau du mme type que from.

toIndex

Premier indice vers lequel les lments seront copis.

count

Nombre dlments copier.

java.util.Arrays 1.2
static void sort(Type[] a)

Trie le tableau en utilisant un algorithme QuickSort adapt.


Paramtres :

Tableau de type int, long, short, char, byte, boolean,


float ou double.

static int binarySearch(Type[] a, Type v)

Utilise lalgorithme BinarySearch pour rechercher la valeur v. Si elle la trouve, la mthode


renvoie lindice de v. Sinon elle renvoie une valeur r ngative ; -r-1 dsigne la position
laquelle v devrait tre insre pour que le tableau reste tri.
Paramtres :

Tableau tri de type int, long, short, char, byte,


boolean, float ou double.

Valeur de mme type que les lments de a.

static void fill(Type[] a, Type v)

Affecte la valeur de v tous les lments du tableau.


Paramtres :

Tableau de type int, long, short, char, byte, boolean,


float ou double.

Valeur de mme type que les lments de a.

static boolean equals(Type[] a, Type[] b)

Renvoie true si les tableaux ont la mme longueur et les lments dindices correspondants
possdent la mme valeur.

Livre Java .book Page 102 Jeudi, 25. novembre 2004 3:04 15

102

Au cur de Java 2 - Notions fondamentales

Paramtres :
a, b
float ou double.

Tableau de type int, long, short, char, byte, boolean,

Tableaux multidimensionnels
Les tableaux multidimensionnels utilisent plusieurs indices pour accder aux lments du tableau.
Ils sont utiliss pour les tables et autres organisations plus complexes. Vous pouvez sauter cette
section jusqu ce que vous ayez besoin dun tel mcanisme de stockage.
Supposons que vous dsiriez crer un tableau de nombres qui montre la manire dont un investissement de 10 000 euros crot selon divers taux dintrt, lorsque les intrts sont pays annuellement
et rinvestis. Le Tableau 3.8 illustre ce scnario.
Tableau 3.8 : Accroissement dun investissement en fonction de diffrents taux dintrt

10 %

11 %

12 %

13 %

14 %

15 %

10 000,00

10 000,00

10 000,00

10 000,00

10 000,00

10 000,00

11 000,00

11 100,00

11 200,00

11 300,00

11 400,00

11 500,00

12 100,00

12 321,00

12 544,00

12 769,00

12 996,00

13 225,00

13 310,00

13 676,31

14 049,28

14 428,97

14 815,44

15 208,75

14 641,00

15 180,70

15 735,19

16 304,74

16 889,60

17 490,06

16 105,10

16 850,58

17 623,42

18 424,35

19 254,15

20 113,57

17 715,61

18 704,15

19 738,23

20 819,52

21 949,73

23 130,61

19 487,17

20 761,60

22 106,81

23 526,05

25 022,69

26 600,20

21 435,89

23 045,38

24 759,63

26 584,44

28 525,86

30 590,23

23 579,48

25 580,37

27 730,79

30 040,42

32 519,49

35 178,76

La manire vidente de stocker cette information est un tableau deux dimensions (ou matrice) que
nous appellerons balance.
Il est trs facile de dclarer une matrice :
double[][] balance;

Comme toujours en Java, vous ne pouvez pas utiliser le tableau avant de lavoir initialis par un
appel new. Linitialisation peut se faire par une instruction comme celle-ci :
balances = new double[NYEARS][NRATES];

Si vous connaissez les lments du tableau, vous pouvez utiliser un raccourci pour initialiser les
tableaux plusieurs dimensions sans avoir recours new. Par exemple :
int[][] magicSquare =
{

Livre Java .book Page 103 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

103

{16, 3, 2, 13},
{5, 10, 11, 8},
{9, 6, 7, 12},
{4, 15, 14, 1}
};

Lorsque le tableau est initialis, vous pouvez accder ses lments individuellement, laide de
deux indices entre crochets, par exemple balance[i][j].
Lexemple de programme stocke un tableau une dimension interest, pour les taux dintrt, et un
tableau deux dimensions balance, pour les soldes des comptes, pour chaque anne et chaque taux
dintrt. La premire ligne du tableau est initialise avec le solde initial :
for (int j = 0; j < balance[0].length; j++)
balances[0][j] = 10000;

Puis les autres lignes sont calcules de la faon suivante :


for (int i = 1; i < balances.length; i++)
{
for (int j = 0; j < balances[i].length; j++)
{
double oldBalance = balances[i - 1][j];
double interest = . . .;
balance[i][j] = oldBalance+interest;
}
}

LExemple 3.8 prsente le programme qui calcule lensemble des valeurs du tableau.
INFO
Une boucle "for each" ne traverse pas automatiquement toutes les entres dun tableau bidimensionnel. Il parcourt
plutt les lignes, qui sont elles-mmes des tableaux une dimension. Pour visiter tous les lments dun tableau bidimensionnel, imbriquez deux boucles, comme ceci :
for (double[] row: balances)
for (double b: row)
faire quelque chose avec b

Exemple 3.8 : CompoundInterest.java


public class CompoundInterest
{
public static void main(String[] args)
{
final int STARTRATE = 10;
final int NRATES = 6;
final int NYEARS = 10;
// dfinir les taux dintrt de 10 15%
double[] interestRate = new double[NRATES];
for (int j = 0; j < interestRate.length; j++)
interestRate[j] = (STARTRATE+j) / 100.0;
double[][] balance = new double[NYEARS][NRATES];
// dfinir les soldes initiaux 10000
for (int j = 0; j < balance[0].length; j++)

Livre Java .book Page 104 Jeudi, 25. novembre 2004 3:04 15

104

Au cur de Java 2 - Notions fondamentales

balance[0][j] = 10000;
// calculer lintrt des annes venir
for (int i = 1; i < balance.length; i++)
{
for (int j = 0; j < balance[i].length; j++)
{
// rcup. solde anne prcdente de la ligne prcdente
double oldBalance = balance[i - 1][j];
// calculer lintrt
double interest = oldBalance * interestRate[j];
// calculer le solde de lanne
balances[i][j] = oldBalance+interest;
}
}
// imprimer une ligne de taux dintrt
for (int j = 0; j < interestRate.length; j++)
System.out.printf("%9.0f%%", 100 * interestRate[j]));
System.out.println();
// imprimer la table des soldes
for (double[] row: balances)
{
// imprimer une ligne de la table
for (double b: row)
System.out.printf("%10.2f", b);
System.out.println();
}
}
}

Tableaux irrguliers
Pour linstant, ce que nous avons vu ne sloigne pas trop des autres langages de programmation.
Mais en ralit il se passe en coulisse quelque chose de subtil que vous pouvez parfois faire tourner
votre avantage : Java ne possde pas de tableaux multidimensionnels, mais uniquement des
tableaux unidimensionnels. Les tableaux multidimensionnels sont en ralit des "tableaux de
tableaux".

Livre Java .book Page 105 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

105

Ainsi, dans lexemple prcdent, balances est en fait un tableau de dix lments, chacun deux
tant un tableau de six nombres rels (voir Figure 3.17).
Figure 3.17

10000.0
10000.0
10000.0
10000.0
10000.0
10000.0

balances =

Un tableau deux
dimensions.

balances[1] =

balances[1][2] =

11000.0
11100.0
11200.0
11300.0
11400.0
11500.0

.
.
.
23579.48
25580.37
27730.79
30040.42
32519.49
35178.76

Lexpression balances[i] se rfre au sous-tableau dindice i, autrement dit la range i; et


balance[i] [j] fait rfrence llment j de ce sous-tableau.
Comme les ranges de tableau sont accessibles individuellement, vous pouvez aisment les intervertir !
double[] temp = balance[i];
balances[i] = balances[i+1];
balances[i+1] = temp;

De plus, il est facile de crer des tableaux "irrguliers", cest--dire des tableaux dont les diffrentes
ranges ont des longueurs diffrentes. Pour illustrer ce mcanisme, crons un tableau dont les
ranges i et les colonnes j reprsentent le nombre de tirages possibles pour une loterie o il fait
"choisir j nombres parmi i nombres" :
1
1
1
1
1
1
1

1
2
3
4
5
6

1
3
6
10
15

1
4
10
20

1
5
15

1
6

Comme j ne peut jamais tre plus grand que i, nous obtenons une matrice triangulaire. La ime
range possde i+1 lments (nous admettons un choix de 0 lment ; et il ny a quune manire

Livre Java .book Page 106 Jeudi, 25. novembre 2004 3:04 15

106

Au cur de Java 2 - Notions fondamentales

deffectuer un tel choix). Pour crer ce tableau irrgulier, commenons par allouer le tableau qui
contient les ranges :
int[][] odds = new int[NMAX+1][];

Crons ensuite les ranges elles-mmes :


for (int n = 0; n <= NMAX; n++)
odds[n] = new int[n+1];

Une fois le tableau allou, nous pouvons accder normalement ses lments, condition de ne pas
dpasser les limites de chaque sous-tableau :
for (int n = 0; n < odds.length; n++)
for (k = 0; k < odds[n].length; k++)
{
// calculer lotteryOdds
. . .
odds[n][k] = lotteryOdds;
}

LExemple 3.9 vous montre le programme complet.


INFO C++
La dclaration Java
double[][] balances = new double[10][6]; // Java

nest pas quivalente


double balances[10][6]; // C++

ni mme
double (*balances)[6] = new double[10][6]; // C++

En fait, en C++, un tableau de 10 pointeurs est allou :


double** balances = new double*[10]; // C++

A chaque lment du tableau de pointeurs est ensuite affect un tableau de 6 nombres :


for (i = 0; i < 10; i++)
balances[i] = new double[6];

Cette boucle est heureusement excute automatiquement par linstruction new double[10][6]. Si vous dsirez
crer un tableau irrgulier, il faut allouer les ranges sparment.

Exemple 3.9 : LotteryArray.java


public class LotteryArray
{
public static void main(String[] args)
{
final int NMAX = 10;
// allouer un tableau triangulaire
int[][] odds = new int[NMAX+1][];
for (int n = 0; n <= NMAX; n++)
odds[n] = new int[n+1];

Livre Java .book Page 107 Jeudi, 25. novembre 2004 3:04 15

Chapitre 3

Structures fondamentales de la programmation Java

// remplir le tableau triangulaire


for (int n = 0; n < odds.length; n++)
for (int k = 0; k < odds[n].length; k++)
{
/*
calculer le binme
n * (n - 1) * (n - 2) * . . . * (n - k+1)
------------------------------------------1 * 2 * 3 * . . . * k
*/
int lotteryOdds = 1;
for (int i = 1; i <= k; i++)
lotteryOdds = lotteryOdds * (n - i+1) / i;
odds[n][k] = lotteryOdds;
}
// imprimer le tableau triangulaire
for (int[] row: odds)
{
for (int odd: row)
System.out.printf("%4d", odd);
System.out.println();
}
}
}

107

Livre Java .book Page 108 Jeudi, 25. novembre 2004 3:04 15

Livre Java .book Page 109 Jeudi, 25. novembre 2004 3:04 15

4
Objets et classes
Au sommaire de ce chapitre

Introduction la programmation oriente objet


Utilisation des classes prdfinies
Construction de vos propres classes
Champs et mthodes statiques
Paramtres des mthodes
Construction dun objet
Packages
Commentaires pour la documentation
Conseils pour la conception de classes
Lobjectif de ce chapitre est :
m

de vous prsenter la programmation oriente objet ;

de vous montrer comment crer des objets appartenant des classes de la bibliothque Java
standard ;

de vous montrer comment rdiger vos propres classes.

Si vous navez pas dexprience en matire de programmation oriente objet, nous vous conseillons
de lire attentivement ce chapitre. La POO (programmation oriente objet) demande une approche
diffrente de celle des langages procduraux. La transition nest pas toujours facile, mais il est
ncessaire de vous accoutumer au concept dobjet avant dapprofondir Java.
Pour les programmeurs C++ expriments, ce chapitre, comme le prcdent, prsentera des informations familires ; malgr tout, il existe des diffrences entre les deux langages, et nous vous
conseillons de lire les dernires sections de ce chapitre (en vous concentrant sur les infos relatives
C++).

Livre Java .book Page 110 Jeudi, 25. novembre 2004 3:04 15

110

Au cur de Java 2 - Notions fondamentales

Introduction la programmation oriente objet


De nos jours, la programmation oriente objet constitue le principal paradigme de la programmation ; elle a remplac les techniques de programmation procdurale, "structure", qui ont t
dveloppes au dbut des annes 1970. Java est totalement orient objet, et il est impossible de
programmer avec ce langage dans le style procdural que vous avez peut-tre appris. Nous esprons
que cette section combine avec les exemples fournis dans le texte et sur le site Web vous fournira assez dinformations sur la POO pour vous permettre de travailler en Java dune manire
productive.
Commenons par une question qui, premire vue, na rien voir avec la programmation : comment
certaines socits de lindustrie informatique sont-elles devenues si importantes, et aussi rapidement ? Nous faisons allusion Compaq, Dell, Gateway et dautres constructeurs dordinateurs
personnels. Certains rpondront quelles fabriquaient en gnral de bonnes machines, vendues bas
prix, dans une priode o la demande atteignait des sommets. Mais allons plus loin : comment ontelles pu construire tant de modles aussi rapidement et comment ont-elles rpondu aussi vite aux
changements qui ont boulevers le march de la micro-informatique ?
La rponse rside essentiellement dans le fait que ces socits ont sous-trait une bonne part de leur
travail. Elles ont achet des lments des vendeurs rputs et se sont charges de lassemblage des
machines. La plupart du temps, elles nont pas investi dargent ni de temps dans la conception et la
fabrication des alimentations, des disques durs, des cartes mres et des autres composants. Ainsi,
elles ont pu produire rapidement des ordinateurs et sadapter trs vite aux nouveauts, tout en rduisant
leurs cots de dveloppement.
Ces constructeurs de micro-ordinateurs achetaient en fait des "fonctionnalits prconditionnes".
Par exemple, lorsquelles achetaient une alimentation, elles acquraient quelque chose qui possdait
certaines proprits (la taille, le poids, etc.) et une certaine fonctionnalit (une sortie stabilise, une
puissance lectrique, etc.). Compaq reprsente un bon exemple de lefficacit de cette mthode.
Lorsque la socit Compaq a abandonn le dveloppement complet de ses machines pour acheter la
plupart de ces lments des tiers, elle a pu amliorer de manire importante le bas de sa gamme.
La POO sappuie sur la mme ide. Votre programme est constitu dobjets possdant certaines
proprits et pouvant accomplir certaines oprations. Cest votre budget et votre temps disponible
qui dcideront du fait que vous construirez un objet ou que vous lachterez. Cependant, tant que les
objets en question satisfont vos spcifications, vous ne vous proccupez pas de savoir comment ils
ont t implments. En POO, vous ntes concern que par ce que les objets vous rvlent. Ainsi,
tout comme les constructeurs dordinateurs ne sintressent pas au dveloppement des alimentations,
la plupart des programmeurs Java ne se proccupent pas de savoir comment un objet est implment,
pourvu quil excute ce quils souhaitent.
La programmation structure traditionnelle consiste concevoir un ensemble de fonctions (ou algorithmes) permettant de rsoudre un problme. Aprs avoir dtermin ces fonctions, ltape suivante
consistait traditionnellement trouver la manire approprie de stocker des donnes. Cest la raison
pour laquelle le concepteur du langage Pascal, Niklaus Wirth, a intitul son fameux ouvrage de
programmation Algorithmes + Structures de donnes = Programmes. Remarquez que le terme algorithmes est plac en tte dans ce titre, devant lexpression structures de donnes. Cela montre bien la
manire dont les programmeurs travaillaient cette poque. Dabord, vous dcidiez de la manire
dont vous alliez manipuler les donnes ; ensuite seulement, vous choisissiez le genre de structures

Livre Java .book Page 111 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

111

que vous imposeriez aux donnes afin de faciliter cette manipulation. La POO inverse cet ordre et
place les donnes au premier plan avant de dterminer lalgorithme qui sera employ pour oprer sur
ces donnes.
En POO, la cl de la productivit consiste rendre chaque objet responsable de laccomplissement
de quelques tches associes. Si un objet dpend dune tche qui nest pas de sa responsabilit, il
doit avoir accs un autre objet capable daccomplir cette tche. Le premier objet demande alors au
second dexcuter la tche en question. Cela seffectue grce une version plus gnralise des
appels de fonctions auxquels vous tes habitus en programmation traditionnelle (notez quen Java
ces appels de fonctions sont appels en gnral des appels de mthodes).
Il faut remarquer quun objet ne doit jamais manipuler directement les donnes internes dun autre
objet, pas plus quil ne doit rendre accessibles directement les donnes aux autres objets. Toute
communication se fait par lintermdiaire dappels de mthodes. Par lencapsulation des donnes
dun objet, vous facilitez leur rutilisation, vous rduisez la dpendance aux donnes et vous minimisez le temps de dbogage.
Bien entendu, comme cest le cas pour les modules dun langage procdural, il ne faut pas quun
objet accomplisse trop de choses. La conception et le dbogage sont simplifis lorsque lon construit
de petits objets spcialiss au lieu dnormes objets contenant des donnes complexes et possdant
des centaines de fonctions pour les manipuler.

Le vocabulaire de la POO
Avant daller plus loin, il faut comprendre certains termes de la POO. Le plus important est le mot
classe, que vous avez dj rencontr dans les exemples du Chapitre 3. Une classe est le modle ou la
matrice de lobjet effectif. Cela nous amne la comparaison habituelle en ce qui concerne les classes :
des moules biscuits, les objets reprsentant les biscuits proprement dits. Lorsquon construit un
objet partir dune classe, on dit que lon cre une instance de cette classe.
Comme vous avez pu le constater, tout le code que vous crivez en Java se trouve dans une classe. La
bibliothque Java standard fournit plusieurs milliers de classes rpondant de multiples besoins
comme la conception de linterface utilisateur, les dates et les calendriers, ou la programmation
rseau. Quoi quil en soit, il vous faut quand mme crer vos propres classes Java, dcrire les objets
des domaines de problmes appartenant vos applications et adapter les classes existantes vos
propres besoins.
Lencapsulation (appele parfois dissimulation des donnes, ou isolement des donnes) est un
concept cl pour lutilisation des objets. Lencapsulation consiste tout bonnement combiner des
donnes et un comportement dans un emballage et dissimuler limplmentation des donnes aux
utilisateurs de lobjet. Les donnes dun objet sont appeles ses champs dinstance ; les fonctions
qui agissent sur les donnes sont appeles ses mthodes. Un objet spcifique, qui est une instance
dune classe, a des valeurs spcifiques dans ses champs dinstance. Le jeu de ces valeurs est ltat
actuel de lobjet. Chaque fois que vous appelez un message sur un objet, son tat peut changer.
Il faut insister sur le fait que lencapsulation ne fonctionne correctement que si les mthodes nont
jamais accs directement aux champs dinstance dans une classe autre que la leur propre. Les
programmes doivent interagir avec les donnes dun objet uniquement par lintermdiaire des
mthodes de lobjet. Lencapsulation reprsente le moyen de donner lobjet son comportement de
"bote noire" ; cest sur elle que reposent la rutilisation et la scurit de lobjet. Cela signifie quune

Livre Java .book Page 112 Jeudi, 25. novembre 2004 3:04 15

112

Au cur de Java 2 - Notions fondamentales

classe peut compltement modifier la manire dont elle stocke ses donnes, mais tant quelle continue utiliser les mmes mthodes pour les manipuler, les autres objets nen sauront rien et ne sen
proccuperont pas.
Lorsque vous commencez rellement crire vos propres classes en Java, un autre principe de la
POO facilite cette opration : les classes peuvent tre construites sur les autres classes. On dit quune
classe construite partir dune autre ltend. Java est en fait fourni avec une "superclasse cosmique"
appele Object. Toutes les autres classes tendent cette classe. Vous en apprendrez plus concernant
la classe Object dans le prochain chapitre.
Lorsque vous tendez une classe existante, la nouvelle classe possde toutes les proprits et mthodes de la classe que vous tendez. Vous fournissez les nouvelles mthodes et les champs de donnes
qui sappliquent uniquement votre nouvelle classe. Le concept dextension dune classe pour en
obtenir une nouvelle est appel hritage. Reportez-vous au prochain chapitre pour plus de dtails
concernant la notion dhritage.

Les objets
Pour bien travailler en POO, vous devez tre capable didentifier trois caractristiques essentielles
des objets. Ce sont :
m

Le comportement de lobjet. Que pouvez-vous faire avec cet objet, ou quelles mthodes pouvezvous lui appliquer ?

Ltat de lobjet. Comment lobjet ragit-il lorsque vous appliquez ces mthodes ?

Lidentit de lobjet. Comment lobjet se distingue-t-il des autres qui peuvent avoir le mme
comportement et le mme tat ?

Tous les objets qui sont des instances dune mme classe partagent le mme comportement. Celui-ci
est dtermin par les mthodes que lobjet peut appeler.
Ensuite, chaque objet stocke des informations sur son aspect actuel. Cest ltat de lobjet. Ltat
dun objet peut changer dans le temps, mais pas spontanment. Une modification dans ltat dun
objet doit tre la consquence dappels de mthodes (si ltat de lobjet change sans quun appel de
mthode soit intervenu, cela signifie que la rgle de lencapsulation a t viole).
Nanmoins, ltat dun objet ne dcrit pas compltement celui-ci, car chaque objet possde une identit spcifique. Par exemple, dans un systme de traitement de commandes, deux commandes sont
distinctes mme si elles dsignent des produits identiques. Remarquez que des objets individuels
instances dune mme classe ont toujours une identit distincte et gnralement un tat distinct.
Chacune de ces caractristiques essentielles peut avoir une influence sur les autres. Par exemple,
ltat dun objet peut altrer son comportement. Si une commande est "expdie" ou "paye", elle
peut refuser un appel de mthode qui demanderait dajouter ou de supprimer un lment. Inversement, si une commande est "vide" autrement dit, si aucun produit na encore t command
elle ne doit pas pouvoir tre expdie.
Dans un programme procdural traditionnel, le processus commence par une fonction principale
main. Lorsquon travaille dans un systme orient objet, il ny a pas de "dbut", et les programmeurs
dbutants en POO se demandent souvent par o commencer. La rponse est la suivante : trouvez
dabord les classes appropries, puis ajoutez des mthodes ces classes.

Livre Java .book Page 113 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

113

ASTUCE
Une rgle simple dans lidentification des classes consiste rechercher des noms quand vous analysez le problme.
En revanche, les mthodes sont symbolises par des verbes.

Par exemple, voici quelques noms dans un systme de gestion de commandes :


m

produit ;

commande ;

adresse de livraison ;

rglement ;

compte.

Ces noms permettent de rechercher les classes Item, Order, et ainsi de suite.
On cherche ensuite les verbes. Les produits (ou articles) sont ajouts aux commandes. Les commandes sont expdies ou annules. Les rglements sont appliqus aux commandes. Tous ces verbes,
"ajouter", "expdier", "annuler" et "appliquer", permettent didentifier lobjet qui aura la principale
responsabilit de leur excution. Par exemple, lorsquun nouveau produit est ajout une
commande, cest lobjet Order (commande) qui doit tre responsable de cet ajout, car il sait
comment stocker et trier ses propres lments. Autrement dit, dans la classe Order, add (ajouter)
doit tre une mthode qui reoit un objet Item (produit) comme paramtre.
Bien entendu, la "rgle des noms et des verbes" nest quune rgle mnmonique, et seule lexprience vous apprendra dterminer quels noms et quels verbes sont importants pour le dveloppement
de vos propres classes.

Relations entre les classes


Les relations les plus courantes entre les classes sont :
m

dpendance ("utilise") ;

agrgation ("possde") ;

hritage ("est").

La relation de dpendance ou "utilise" est la plus vidente et la plus courante. Par exemple, la classe
Order utilise la classe Account, car les objets Order doivent pouvoir accder aux objets Account
pour vrifier que le compte est crdit. Mais la classe Item ne dpend pas de la classe Account, car
les objets Item nont pas se proccuper de ltat du compte dun client. Une classe dpend dune
autre classe si ses mthodes manipulent des objets de cette classe.
ASTUCE
Efforcez-vous de rduire le nombre de classes qui dpendent mutuellement les unes des autres. Lavantage est le
suivant : si une classe A ignore lexistence dune classe B, elle naura pas se proccuper des modifications apportes
ventuellement B! (Et cela signifie quune modification dans la classe B nintroduira pas de bogues dans la classe A.)
Dans la terminologie logicielle, on dit vouloir rduire le couplage entre les classes.

Livre Java .book Page 114 Jeudi, 25. novembre 2004 3:04 15

114

Au cur de Java 2 - Notions fondamentales

La relation dagrgation ou "possde" est facile comprendre, car elle est concrte ; par exemple, un
objet Order contient des objets Item. Cette relation signifie que des objets dune classe A contiennent
des objets dune classe B.
INFO
Certains ddaignent le concept dagrgation et prfrent parler de relation "dassociation". Du point de vue de la
modlisation, cela peut se comprendre. Mais pour les programmeurs, la relation "possde" semble vidente. Nous
prfrons personnellement le terme agrgation pour une seconde raison : la notation standard pour les associations
est moins claire. Voir le Tableau 4.1.

La relation dhritage ou "est" exprime une relation entre une classe plus spcifique et une, plus
gnrale. Par exemple, une classe RushOrder (commande urgente) hrite dune classe Order. La
classe spcialise RushOrder dispose de mthodes particulires pour grer la priorit et dune
mthode diffrente pour calculer le cot de livraison, mais ses autres mthodes par exemple,
ajouter des lments ou facturer sont hrites de la classe Order. En gnral, si la classe A tend
la classe B, la classe A hrite des mthodes de la classe B, mais possde des fonctionnalits supplmentaires (lhritage sera dcrit plus en dtail au chapitre suivant).
De nombreux programmeurs ont recours la notation UML (Unified Modeling Language) pour
reprsenter les diagrammes de classe qui dcrivent la relation entre les classes. Un exemple est
montr la Figure 4.1. Vous dessinez les classes sous la forme de rectangles, et les relations sont
reprsentes par des flches ayant diffrents aspects. Le Tableau 4.1 montre les styles de flches les
plus couramment utiliss.
Figure 4.1
Diagramme dune classe.

INFO
Plusieurs outils permettent de dessiner ce type de diagramme. Les fournisseurs proposent souvent des outils performants (et chers) censs tre le point central de la procdure de dveloppement. On trouve entre autres Rational Rose

Livre Java .book Page 115 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

115

(http://www.ibm.com/software/awdtools/developer/modeler) et Together (http://www.borland.com/together).


Vous pouvez galement opter pour le programme source libre ArgoUML (http://argouml.tigris.org). Une version
commerciale est disponible chez GentleWare (http://gentleware.com). Pour dessiner des diagrammes simples sans
trop de problmes, testez Violet (http://horstmann.com/violet).

Tableau 4.1 : Notation UML pour reprsenter la relation entre classes

Relation

Connecteur UML

Hritage
Hritage dinterface
Dpendance
Agrgation
Association
Association dirige

Comparaison entre POO et programmation procdurale traditionnelle


Nous terminerons cette courte introduction la POO en comparant celle-ci avec le modle procdural qui doit vous tre familier. En programmation procdurale, on identifie dabord les tches
accomplir, puis :
m

En procdant par tapes, on rduit chaque tche en sous-tches, puis celles-ci en sous-tches
plus petites, et ainsi de suite jusqu ce que les sous-tches obtenues soient suffisamment simples
pour tre implmentes directement (approche de haut en bas).

On crit des procdures permettant de rsoudre les tches simples, puis on les combine en procdures plus sophistiques jusqu ce que lon obtienne les fonctionnalits souhaites (approche de
bas en haut).

Bien entendu, la plupart des programmeurs emploient un mlange de ces deux stratgies pour rsoudre un problme de programmation. La rgle de base pour dcouvrir des procdures est identique
celle que lon utilise pour trouver les mthodes en POO : chercher les verbes ou les actions dans la
description du problme. La diffrence principale rside dans le fait quen POO on isole dabord les
classes dans le projet. Cest ensuite seulement que lon cherche les mthodes. Il existe une autre
distinction dimportance entre les procdures traditionnelles et les mthodes de la POO : chaque
mthode est associe la classe qui est responsable de lopration.
Pour de petits problmes, la rduction en procdures fonctionne trs bien. Pour des problmes
plus importants, les classes et les mthodes offrent deux avantages. Les classes fournissent un
mcanisme de regroupement des mthodes, qui est trs pratique. Limplmentation dun simple
navigateur Web peut exiger soit 2 000 fonctions, soit 100 classes possdant en moyenne
20 mthodes. Cette deuxime structure est plus facile matriser pour un programmeur et aussi
rpartir parmi les membres dune quipe. Lencapsulation offre galement une aide apprciable :

Livre Java .book Page 116 Jeudi, 25. novembre 2004 3:04 15

116

Au cur de Java 2 - Notions fondamentales

les classes dissimulent la reprsentation de leurs donnes tout le programme, except leurs
propres mthodes. Comme le montre la Figure 4.2, cela signifie que, si un bogue altre des
donnes, il est plus ais de rechercher le coupable parmi les 20 mthodes qui ont accs ces
donnes que parmi 2 000 procdures.
Vous pourriez penser que tout cela ne semble pas trs diffrent de la modularisation. Vous avez sans
doute crit des programmes que vous avez diviss en modules qui communiquent laide de procdures plutt quen changeant des donnes. Lorsque cette technique est bien applique, elle se
rapproche normment de lencapsulation. Nanmoins, dans la plupart des langages, la moindre
ngligence vous permet daccder aux donnes dun autre module lencapsulation est facile
contourner.
Il existe un problme plus srieux : alors que les classes reprsentent des usines capables de produire
de nombreux objets ayant le mme comportement, il nest pas possible dobtenir de multiples copies
dun module utile. Supposons que vous disposiez dun module qui encapsule une collection de
commandes et dun autre module contenant un arbre binaire bien quilibr pour accder ces
commandes. Supposons encore que vous ayez besoin de deux collections, une pour les commandes
en attente et lautre pour les commandes termines. Vous ne pouvez pas lier deux fois le module
darbre binaire. Et vous navez srement pas envie den faire une copie et de renommer toutes les
procdures pour permettre au lieur de fonctionner !
Les classes ne connaissent pas de telles limites. Lorsquune classe a t dfinie, il est trs facile de
construire nimporte quel nombre dinstances de cette classe (alors quun module ne peut avoir
quune seule instance).
Figure 4.2
La programmation
procdurale compare
la POO.

procdure
mthode
mthode

procdure
procdure

Donnes
globales

mthode
mthode

procdure
procdure

mthode
mthode

Donnes
objet

Donnes
objet

Donnes
objet

Nous navons encore fait que gratter lgrement la surface. Vous trouverez la fin de ce chapitre une
petite section contenant des astuces pour concevoir les classes. Toutefois, pour une meilleure
comprhension du processus de conception oriente objet, de nombreux ouvrages existent sur le
sujet.

Utilisation des classes existantes


On ne peut rien faire en Java sans les classes, et vous avez dj vu plusieurs classes dans les chapitres
prcdents. Malheureusement, la plupart dentre elles ne correspondent pas lesprit de Java.

Livre Java .book Page 117 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

117

Un bon exemple de cette anomalie est constitu par la classe Math. Vous avez vu que lon peut utiliser les mthodes de la classe Math, telles que Math.random, sans avoir besoin de savoir comment
elles sont implmentes il suffit den connatre le nom et les paramtres (sil y en a). Cest la
caractristique de lencapsulation, et ce sera vrai pour toutes les classes. Malheureusement, la classe
Math encapsule seulement une fonctionnalit ; elle na pas besoin de manipuler ou de cacher des
donnes. Comme il ny a pas de donnes, vous navez pas vous proccuper de la cration des objets
et de linitialisation de leurs champs dinstance il ny en a pas !
Dans la prochaine section, nous allons tudier une classe plus typique, la classe Date. Vous verrez
comment construire les objets et appeler les mthodes de cette classe.

Objets et variables objet


Pour travailler avec les objets, le processus consiste crer des objets et spcifier leur tat initial.
Vous appliquez ensuite les mthodes aux objets.
Dans le langage Java, on utilise des constructeurs pour construire de nouvelles instances. Un
constructeur est une mthode spciale dont le but est de construire et dinitialiser les objets.
Prenons un exemple. La bibliothque Java standard contient une classe Date. Ses objets dcrivent des
moments prcis dans le temps, tels que "31 dcembre 1999, 23:59:59 GMT".
INFO
Vous vous demandez peut-tre pourquoi utiliser des classes pour reprsenter des dates au lieu (comme dans certains
langages) dun type intgr. Visual Basic, par exemple, possde un type de donnes intgr et les programmeurs
peuvent spcifier les dates au format #6/1/1995#. Cela semble apparemment pratique ; les programmeurs utilisent simplement ce type sans se proccuper des classes. Mais en ralit, cette conception de Visual Basic convient-elle
dans tous les cas ? Avec certains paramtres locaux, les dates sont spcifies sous la forme mois/jour/anne, dans
dautres sous la forme jour/mois/anne. Les concepteurs du langage sont-ils vraiment arms pour prvoir tous ces cas
de figure ? Sils font un travail incomplet, le langage devient dsagrablement confus et le programmeur frustr est
impuissant. Avec les classes, la tche de conception est dlgue un concepteur de bibliothque. Si une classe nest
pas parfaite, les programmeurs peuvent facilement crire la leur pour amliorer ou remplacer les classes du systme.

Les constructeurs ont toujours le mme nom que la classe. Par consquent, le constructeur pour la
classe Date est appel Date. Pour construire un objet Date, vous combinez le constructeur avec
loprateur new de la faon suivante :
new Date()

Cette expression construit un nouvel objet. Lobjet est initialis avec lheure et la date courantes.
Vous pouvez aussi passer lobjet une mthode :
System.out.println(new Date());

Une autre possibilit consiste appliquer une mthode lobjet que vous venez de construire. Lune
des mthodes de la classe Date est la mthode toString. Cette mthode permet dobtenir une reprsentation au format chane de la date. Voici comment appliquer la mthode toString un objet
Date nouvellement construit :
String s = new Date().toString();

Livre Java .book Page 118 Jeudi, 25. novembre 2004 3:04 15

118

Au cur de Java 2 - Notions fondamentales

Dans ces deux exemples, lobjet construit nest utilis quune seule fois. Gnralement, vous voulez
conserver les objets que vous construisez pour pouvoir continuer les utiliser. Stockez simplement
lobjet dans une variable :
Date birthday = new Date();

La Figure 4.3 montre la variable objet birthday (anniversaire) faisant rfrence lobjet qui vient
dtre construit.
Figure 4.3

birthday =

Cration
dun nouvel objet.

Date

Il existe une diffrence importante entre les objets et les variables objet. Par exemple, linstruction
Date deadline; // deadline ne dsigne pas un objet

dfinit une variable objet, deadline (date limite) qui peut rfrencer des objets de type Date. Il est
important de comprendre que la variable deadline nest pas un objet et quen fait elle ne rfrence
encore aucun objet. Pour le moment, vous ne pouvez employer aucune mthode Date avec cette
variable. Linstruction
s = deadline.toString(); // pas encore

provoquerait une erreur de compilation.


Vous devez dabord initialiser la variable deadline. Pour cela, deux possibilits. Vous pouvez, bien
entendu, initialiser la variable avec lobjet nouvellement construit :
deadline = new Date();

Ou bien dfinir la variable pour quelle fasse rfrence un objet existant :


deadline = birthday;

Maintenant, les deux variables font rfrence au mme objet (voir Figure 4.4).
Figure 4.4
Variables objet
faisant rfrence
au mme objet.

birthday =

Date

deadline =

Il est important de comprendre quune variable objet ne contient pas rellement un objet. Elle fait
seulement rfrence un objet.
En Java, la valeur de toute variable objet est une rfrence un objet qui est stock ailleurs. La valeur
renvoye par loprateur new est aussi une rfrence. Une instruction telle que
Date deadline = new Date();

Livre Java .book Page 119 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

119

comprend deux parties. Lexpression new Date() cre un objet du type Date, et sa valeur est une
rfrence cet objet qui vient dtre cr. Cette rfrence est ensuite stocke dans la variable
deadline.
Il est possible de donner explicitement la valeur null une variable objet afin dindiquer quelle ne
rfrence actuellement aucun objet :
deadline = null;
. . .
if (deadline!= null)
System.out.println(deadline);

Une erreur dexcution se produit si vous appliquez une mthode une variable ayant la valeur
null:
birthday = null;
String s = birthday.toString(); // erreur lexcution!

Les variables ne sont pas initialises automatiquement null. Vous devez les initialiser, soit en
appelant new, soit en leur affectant null.
INFO C++
De nombreuses personnes pensent tort que les variables objet de Java se comportent comme les rfrences de C++.
Mais en C++ il ny a pas de rfrences nulles et les rfrences ne peuvent pas tre affectes. Il faut plutt penser aux
variables objet de Java comme aux pointeurs sur objets de C++. Par exemple,
Date birthday; // Java

est en fait identique :


Date* birthday; // C++

Lorsque lon fait cette association, tout redevient clair. Bien entendu, un pointeur Date* nest pas initialis tant que
lon nappelle pas new. La syntaxe est presque la mme en C++ et en Java :
Date* birthday = new Date(); // C++

Si lon copie une variable dans une autre, les deux variables font rfrence la mme date : ce sont des pointeurs sur
le mme objet. Lquivalent dune rfrence null de Java est le pointeur null de C++.
Tous les objets Java rsident dans le tas (heap). Lorsquun objet contient une autre variable objet, cette variable ne
contient elle-mme quun pointeur sur un autre objet qui rside dans le tas.
En C++, les pointeurs vous rendent nerveux, car ils sont responsables de nombreuses erreurs. Il est trs facile de
crer des pointeurs incorrects ou daltrer la gestion de la mmoire. En Java, ces problmes ont tout bonnement
disparu. Si vous utilisez un pointeur qui nest pas initialis, le systme dexcution dclenchera une erreur dexcution au lieu de produire des rsultats alatoires. Vous navez pas vous proccuper de la gestion de la mmoire, car
le rcuprateur de mmoire (ou ramasse-miettes) sen charge.
En prenant en charge les constructeurs de copie et les oprateurs daffectation, le C++ a fait un bel effort pour
permettre limplmentation dobjets qui se copient automatiquement. Par exemple, une copie dune liste lie est
une nouvelle liste lie ayant un contenu identique, mais des liens indpendants. Ce mcanisme autorise la conception de classes ayant le mme comportement que les classes prdfinies. En Java, il faut utiliser la mthode clone
pour obtenir une copie complte dun objet.

Livre Java .book Page 120 Jeudi, 25. novembre 2004 3:04 15

120

Au cur de Java 2 - Notions fondamentales

La classe GregorianCalendar de la bibliothque Java


Dans les exemples prcdents, nous avons employ la classe Date qui fait partie de la bibliothque
Java standard. Une instance de cette classe possde un tat une position dans le temps.
Bien quil ne soit pas indispensable de connatre ces dtails pour utiliser la classe Date, lheure est
reprsente par le nombre de millimes de seconde (positif ou ngatif) partir dun point fixe
(appel epoch ou poque), qui est le 1er janvier 1970 00:00:00 UTC. UTC est le temps universel
(Coordinated Universal Time), le standard scientifique qui est, pour des raisons pratiques, le mme
que lheure GMT (Greenwich Mean Time).
En ralit, la classe Date nest pas trs pratique pour manipuler les dates. Les concepteurs de la
bibliothque Java considrent quune description de date telle que "31 dcembre 1999, 23:59:59"
est une convention arbitraire dtermine par un calendrier. Cette description correspond celle
du calendrier grgorien utilis dans de nombreux pays. Ce mme repre temporel pourrait tre dcrit
diffremment dans le calendrier chinois ou le calendrier lunaire hbreu, sans parler du calendrier de
nos clients martiens.
INFO
Au cours de lhistoire de lhumanit, les civilisations se sont dbattues avec la conception de calendriers attribuant
des noms aux dates, et ont mis de lordre dans les cycles solaires et lunaires. Louvrage Calendrical Calculations, de
Nachum Dershowitz et Edward M. Reingold (Cambridge University Press, 1997), fournit une explication fascinante
des calendriers dans le monde, du calendrier rvolutionnaire franais celui des Mayas.

Les concepteurs de la bibliothque ont dcid de sparer le fait de conserver le temps et dattacher
des noms des points temporels. La bibliothque Java standard contient donc deux classes distinctes : la classe Date qui reprsente un point temporel, et la classe GregorianCalendar qui exprime
les dates par rapport au calendrier. En fait, la classe GregorianCalendar tend une classe Calendar
plus gnrique, qui dcrit les proprits des calendriers en gnral. Thoriquement, vous pouvez
tendre la classe Calendar et implmenter le calendrier lunaire chinois ou un calendrier martien.
Quoi quil en soit, la bibliothque standard ne contient pas dautre implmentation de calendrier que
le calendrier grgorien.
Distinguer la mesure du temps de la notion de calendriers relve tout fait de la conception oriente
objet. Il est gnralement souhaitable dutiliser des classes spares pour exprimer des concepts
diffrents.
La classe Date ne dispose que dun petit nombre de mthodes pour comparer deux points dans le
temps. Par exemple, les mthodes before et after vous indiquent si un moment donn vient avant
ou aprs un autre :
if (today.before(birthday))
System.out.println("I still have time to shop for a gift.");

INFO
En ralit, la classe Date dispose de mthodes telles que getDay, getMonth et getYear, mais ces mthodes sont
dprcies. Cela signifie que le concepteur de la bibliothque a admis quelles nauraient jamais d y figurer.

Livre Java .book Page 121 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

121

Ces mthodes faisaient partie de la classe Date avant que les concepteurs ralisent quil tait plus logique de fournir
des classes de calendrier spares. Lors de lintroduction de ces classes, les mthodes Date ont t dprcies. Vous
pouvez toujours les employer dans vos programmes, mais vous obtiendrez alors des avertissements disgracieux du
compilateur. Il est prfrable dviter lutilisation de mthodes dprcies, car elles peuvent trs bien tre supprimes
dans une version future de la bibliothque.

La classe GregorianCalendar propose beaucoup plus de mthodes que la classe Date. Elle possde
en particulier plusieurs constructeurs utiles. Lexpression
new GregorianCalendar()

construit un nouvel objet qui reprsente la date et lheure de construction de lobjet.


Vous pouvez construire un objet calendrier pour minuit une date spcifique en fournissant lanne,
le mois et le jour :
new GregorianCalendar(1999, 11, 31)

Assez curieusement, les mois sont compts partir de 0. Ainsi, le mois 11 est dcembre. Pour
simplifier ces manipulations, il existe des constantes comme Calendar.DECEMBER:
new GregorianCalendar(1999, Calendar.DECEMBER, 31)

Vous pouvez aussi dfinir lheure :


new GregorianCalendar(1999, Calendar.DECEMBER, 31, 23, 59, 59)

Vous stockez bien entendu lobjet une fois construit dans une variable objet :
GregorianCalendar deadline = new GregorianCalendar(. . .);

La classe GregorianCalendar a des champs dinstance encapsuls pour conserver la date sa


valeur dfinie. Si lon nexamine pas le code source, il est impossible de connatre la reprsentation
interne de ces donnes dans la classe. Evidemment, cest justement l lintrt de la chose. Ce qui
importe, ce sont les mthodes mises disposition par la classe.

Les mthodes daltration et les mthodes daccs


Vous vous demandez probablement comment obtenir le jour, le mois ou lanne de la date encapsule dans un objet GregorianCalendar. Et comment modifier les valeurs si elles ne vous conviennent pas. Vous trouverez les rponses ces questions en consultant la documentation en ligne ou les
infos API la fin de cette section. Nous allons tudier ici les mthodes les plus importantes.
Le rle dun calendrier est de calculer les attributs, tels que la date, le jour de la semaine, le mois ou
lanne, dun point donn dans le temps. La mthode get de la classe GregorianCalendar permet
dextraire ces donnes. Pour slectionner llment que vous souhaitez connatre, vous passez la
mthode une des constantes dfinies dans la classe Calendar, comme Calendar.MONTH pour le
mois, ou Calendar.DAY_OF_WEEK pour le jour de la semaine :
GregorianCalendar now = new GregorianCalendar();
int month = now.get(Calendar.MONTH);
int weekday = now.get(Calendar.DAY_OF_WEEK);

Les infos API donnent la liste de toutes les constantes disponibles.

Livre Java .book Page 122 Jeudi, 25. novembre 2004 3:04 15

122

Au cur de Java 2 - Notions fondamentales

Il est possible de changer ltat en appelant la mthode set:


deadline.set(Calendar.YEAR, 2001);
deadline.set(Calendar.MONTH, Calendar.APRIL);
deadline.set(Calendar.DAY_OF_MONTH, 15);

Il existe aussi une mthode pour dfinir lanne, le mois et le jour en un seul appel :
deadline.set(2001, Calendar.APRIL, 15);

Vous pouvez enfin ajouter un certain nombre de jours, de semaines, de mois, etc. un objet de calendrier
donn :
deadline.add(Calendar.MONTH, 3); // dcaler la date limite de 3 mois

Si vous ajoutez un nombre ngatif, le dplacement dans le calendrier se fait en arrire.


Il existe une diffrence conceptuelle entre, dune part, la mthode get et, dautre part, les mthodes
set et add. La mthode get se contente dexaminer ltat de lobjet et de renvoyer une information.
En revanche, les mthodes set et add modifient ltat de lobjet. Les mthodes qui modifient des
champs dinstance sont appeles mthodes daltration (mutator), celles qui se contentent daccder
aux champs de linstance, sans les modifier, sont appeles mthodes daccs (accessor).
INFO C++
En C++, le suffixe const est utilis pour dsigner les mthodes daccs. Une mthode qui nest pas dclare comme
const est suppose tre une mthode daltration. Dans le langage Java, il nexiste cependant pas de syntaxe particulire pour distinguer les mthodes daccs de celles daltration.

Une convention couramment employe consiste prfixer les mthodes daccs laide de get et
les mthodes daltration laide de set. Par exemple, la classe GregorianCalendar dispose des
mthodes getTime et setTime qui rcuprent et dfinissent le moment dans le temps quun objet
calendrier reprsente :
Date time = calendar.getTime();
calendar.setTime(time);

Ces mthodes sont particulirement utiles pour raliser des conversions entre les classes Date et
GregorianCalendar. Supposons, par exemple, que vous connaissiez lanne, le mois et le jour
et que vous vouliez dfinir un objet Date avec ces informations. Puisque la classe Date ne sait rien
des calendriers, nous allons dabord construire un objet GregorianCalendar, puis appeler la
mthode getTime pour obtenir une date :
GregorianCalendar calendar
= new GregorianCalendar(year, month, day);
Date hireDay = calendar.getTime();

Inversement, si vous voulez trouver lanne, le mois ou le jour dun objet Date, vous construisez un
objet GregorianCalendar, dfinissez lheure, puis appelez la mthode get:
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(hireDay);
int year = calendar.get(Calendar.YEAR);

Livre Java .book Page 123 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

123

Nous allons conclure cette section avec un programme qui tire parti de la classe GregorianCalendar.
Le programme affiche un calendrier pour le mois en cours de la faon suivante :
Sun Mon Tue Wed Thu Fri Sat
1
2
3
4
5
6
7
8
9 10 11 12 13 14 15
16 17 18 19* 20 21 22
23 24 25 26 27 28 29
30 31

La date du jour est signale par un *, et le programme sait comment calculer les jours de la semaine.
Examinons les tapes cls du programme. Tout dabord, nous construisons un objet calendrier qui
est initialis avec la date et lheure courantes (en ralit, lheure na pas dimportance pour cette
application) :
GregorianCalendar d = new GregorianCalendar();

Nous capturons le jour et le mois courants en appelant deux fois la mthode get:
int today = d.get(Calendar.DAY_OF_MONTH);
int month = d.get(Calendar.MONTH);

Nous affectons ensuite d le premier jour du mois et rcuprons le jour de la semaine correspondant
cette date :
d.set(Calendar.DAY_OF_MONTH, 1);
int weekday = d.get(Calendar.DAY_OF_WEEK);

La variable weekday est dfinie 1 (ou Calendar.SUNDAY) si le premier jour du mois est un dimanche,
2 (ou Calendar.MONDAY) sil sagit dun lundi, etc.
Nous imprimons ensuite len-tte et les espaces pour lindentation de la premire ligne du calendrier.
Pour chaque jour, nous imprimons un espace si la date est < 10, puis la date, et un * si la date est
celle du jour. Chaque samedi, nous allons la ligne.
Puis nous avanons d de un jour :
d.add(Calendar.DAY_OF_MONTH, 1);

Quand allons-nous nous arrter ? Nous ne savons pas si le mois a 31, 30, 29 ou 28 jours. Nous poursuivons tant que d est dans le mois en cours :
do
{
. . .
}
while (d.get(Calendar.MONTH) == month);

Lorsque d se trouve dans le mois suivant, le programme se termine.


LExemple 4.1 montre le programme complet.
Vous pouvez constater que la classe GregorianCalendar simplifie lcriture dun programme de
calendrier qui prend en charge toute la complexit relative aux jours de la semaine et aux diffrentes
longueurs des mois. Vous navez pas besoin de savoir comment la classe GregorianCalendar
calcule les mois et les jours de la semaine. Vous utilisez simplement linterface de la classe les
mthodes get, set et add.

Livre Java .book Page 124 Jeudi, 25. novembre 2004 3:04 15

124

Au cur de Java 2 - Notions fondamentales

Lintrt de cet exemple de programme est de dmontrer comment vous pouvez utiliser linterface
dune classe pour raliser des tches assez sophistiques, sans jamais avoir vous proccuper des
dtails de son implmentation.
Exemple 4.1 : CalendarTest.java
import java.util.*;
public class CalendarTest
{
public static void main(String[] args)
{
// construire d comme la date courante
GregorianCalendar d = new GregorianCalendar();
int today = d.get(Calendar.DAY_OF_MONTH);
int month = d.get(Calendar.MONTH);
// attribuer d le premier jour du mois
d.set(Calendar.DAY_OF_MONTH, 1);
int weekday = d.get(Calendar.DAY_OF_WEEK);
// imprimer len-tte
System.out.println("Sun Mon Tue Wed Thu Fri Sat");
// indenter la premire ligne du calendrier
for (int i = Calendar.SUNDAY; i < weekday; i++ )
System.out.print("
");
do
{
// imprimer la date
int day = d.get(Calendar.DAY_OF_MONTH);
System.out.printf("%3d", day);
// marquer la date du jour avec un *
if (day == today)
System.out.print("*");
else
System.out.print(" ");
// sauter la ligne aprs chaque samedi
if (weekday == Calendar.SATURDAY)
System.out.println();
// incrmenter d
d.add(Calendar.DAY_OF_MONTH, 1);
weekday = d.get(Calendar.DAY_OF_WEEK);
}
while (d.get(Calendar.MONTH) == month);
// sortir de la boucle si d est le premier jour du mois suivant
// imprimer dernire fin de ligne si ncessaire
if (weekday!= Calendar.SUNDAY)
System.out.println();
}
}

Livre Java .book Page 125 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

125

INFO
A des fins de simplicit, le programme de lExemple 4.1 affiche un calendrier contenant les noms anglais des jours de
la semaine, en supposant que la semaine commence un dimanche. Regardez la classe DateFormatSymbols pour
connatre les noms des jours de la semaine dans dautres langues. La mthode Calendar.getFirstDayOfWeek
renvoie le premier jour de la semaine, par exemple dimanche aux Etats-Unis et lundi en Allemagne.

java.util.GregorianCalendar 1.1
GregorianCalendar()

Construit un objet calendrier reprsentant lheure courante, dans la zone horaire par dfaut et
avec les paramtres locaux par dfaut.

GregorianCalendar(int year, int month, int day)

Construit un calendrier grgorien la date spcifie.


Paramtres :

year

Lanne de la date.

month

Le mois de la date, base 0 (autrement dit : 0 pour janvier).

day

Le jour du mois.

GregorianCalendar(int year, int month, int day, int hour, int minutes,
int seconds)

Construit un calendrier grgorien la date et lheure spcifies.


Paramtres :

year

Lanne de la date.

month

Le mois de la date, base 0 (autrement dit : 0 pour janvier).

day

Le jour du mois.

hour

Lheure (de 0 23).

minutes

Les minutes (de 0 59).

seconds

Les secondes (de 0 59).

int get(int field)

Extrait la valeur du champ spcifi.


Paramtres :

field

Une des valeurs suivantes :


Calendar.ERA
Calendar.YEAR
Calendar.MONTH
Calendar.WEEK_OF_YEAR
Calendar.WEEK_OF_MONTH
Calendar.DAY_OF_MONTH
Calendar.DAY_OF_YEAR
Calendar.DAY_OF_WEEK
Calendar.DAY_OF_WEEK_IN_MONTH
Calendar.AM_PM
Calendar.HOUR

Livre Java .book Page 126 Jeudi, 25. novembre 2004 3:04 15

126

Au cur de Java 2 - Notions fondamentales

Calendar.HOUR_OF_DAY
Calendar.MINUTE
Calendar.SECOND
Calendar.MILLISECOND
Calendar.ZONE_OFFSET
Calendar.DST_OFFSET

void set(int field, int value)


Dfinit la valeur dun champ particulier.
Paramtres :

field

Une des constantes acceptes par get.

value

La nouvelle valeur.

void set(int year, int month, int day)

Dfinit une nouvelle date.


Paramtres :

year

Lanne de la date.

month

Le mois de la date, base 0 (autrement dit : 0 pour janvier).

day

Le jour du mois.

void set(int year, int month, int day, int hour, int minutes, int seconds)

Fournit de nouvelles valeurs pour la date et lheure.


Paramtres :

year

Lanne de la date.

month

Le mois de la date, base 0 (autrement dit : 0 pour janvier).

day

Le jour du mois.

hour

Lheure (de 0 23).

minutes

Les minutes (de 0 59).

seconds

Les secondes (de 0 59).

void add(int field, int amount)

Est une mthode arithmtique. Elle ajoute la quantit spcifie un champ. Par exemple, pour
ajouter 7 jours la date courante, utilisez c.add(Calendar.DAY_OF_MONTH, 7).
Paramtres :

field

Le champ modifier (spcifi laide dune des constantes


acceptes par get).

amount

Quantit ajouter au champ (peut tre ngative).

void setTime(Date time)

Dfinit le calendrier cette position dans le temps.


Paramtres :

time

La position dans le temps.

Date getTime()

Dtermine la position dans le temps reprsente par la valeur de cet objet calendrier.

Livre Java .book Page 127 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

127

Construction de vos propres classes


Vous avez pu voir au Chapitre 3 comment construire des classes simples. Ces classes taient toutes
constitues dune unique mthode main. Il est temps maintenant dtudier lcriture de classes plus
complexes, ncessaires des applications plus sophistiques. Ces classes nont gnralement pas de
mthode main. Elles possdent en revanche leurs propres mthodes et champs dinstance. Pour
construire un programme complet, vous combinez plusieurs classes, dont lune possde une
mthode main.

Une classe Employee


La syntaxe la plus simple dune classe Java est la suivante :
class NomDeClasse
{
constructeur 1
constructeur 2
. . .
mthode1
mthode2
. . .
champ1
champ2
. . .
}

INFO
Nous avons adopt la rgle qui consiste dfinir les mthodes au dbut et placer les champs dinstance la fin
(dune certaine manire, cela encourage peut-tre lide que linterface doit prendre le pas sur limplmentation).

Considrons cette version trs simplifie dune classe Employee qui pourrait tre utilise pour le
registre du personnel dune entreprise :
class Employee
{
// constructeur
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
}
// une mthode
public String getName()
{
return name;
}
// autres mthodes
. . .

Livre Java .book Page 128 Jeudi, 25. novembre 2004 3:04 15

128

Au cur de Java 2 - Notions fondamentales

// champs dinstance
private String name;
private double salary;
private Date hireDay;
}

Nous analyserons limplmentation de cette classe dans les sections suivantes. Examinez dabord
lExemple 4.2, qui prsente un programme permettant de voir comment on peut utiliser la classe
Employee.
Dans ce programme, nous construisons un Tableau Employee et le remplissons avec trois objets
employee:
Employee[] staff = new Employee[3];
staff[0] = new Employee("Carl Cracker", . . .);
staff[1] = new Employee("Harry Hacker", . . .);
staff[2] = new Employee("Tony Tester", . . .);

Nous utilisons ensuite la mthode raiseSalary de la classe Employee pour augmenter de 5 % le


salaire de chaque employ :
for (Employee e: staff)
e.raiseSalary(5);

Enfin, nous imprimons les informations concernant chaque employ, en appelant les mthodes
getName, getSalary et getHireDay:
for (Employee e: staff)
System.out.println("name="+e.getName()
+",salary="+e.getSalary()
+",hireDay="+e.getHireDay());

Remarquez que ce programme est constitu de deux classes : la classe Employee et une classe
EmployeeTest ayant un modificateur (ou spcificateur daccs) public. La mthode main avec les
instructions que nous venons de dcrire est contenue dans la classe EmployeeTest.
Le nom du fichier source est EmployeeTest.java, puisque le nom du fichier doit tre identique
celui de la classe public. Vous ne pouvez avoir quune classe publique dans un fichier source, mais
le nombre de classes non publiques nest pas limit.
Quand vous compilez ce code source, le compilateur cre deux fichiers classe dans le rpertoire :
EmployeeTest.class et Employee.class.
Vous lancez le programme en donnant linterprteur de bytecode le nom de la classe qui contient la
mthode main de votre programme :
java EmployeeTest

Linterprteur de bytecode dmarre lexcution par la mthode main de la classe EmployeeTest. A son
tour, le code de cette mthode construit trois nouveaux objets Employee et vous montre leur tat.
Exemple 4.2 : EmployeeTest.java
import java.util.*;
public class EmployeeTest
{
public static void main(String[] args)
{

Livre Java .book Page 129 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

// remplir le tableau staff avec trois objets Employee


Employee[] staff = new Employee[3];
staff[0]
1987,
staff[1]
1989,
staff[2]
1990,

= new Employee("Carl Cracker", 75000,


12, 15);
= new Employee("Harry Hacker", 50000,
10, 1);
= new Employee("Tony Tester", 40000,
3, 15);

// augmenter tous les salaires de 5%


for (Employee e: staff)
e.raiseSalary(5);
// afficher les informations concernant
// tous les objets Employee
for (Employee e: staff)
System.out.println("name="+e.getName()
+",salary="+e.getSalary()
+",hireDay="+e.getHireDay());
}
}
class Employee
{
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
// Avec GregorianCalendar 0 dsigne janvier
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
private String name;
private double salary;
private Date hireDay;
}

129

Livre Java .book Page 130 Jeudi, 25. novembre 2004 3:04 15

130

Au cur de Java 2 - Notions fondamentales

Travailler avec plusieurs fichiers source


Le programme de lExemple 4.2 a deux classes dans un seul fichier source. Nombre de programmeurs prfrent avoir un fichier source pour chaque classe. Vous pouvez, par exemple, placer la
classe Employee dans un fichier Employee.java et EmployeeTest dans EmployeeTest.java.
Si vous prfrez cette organisation, deux possibilits vous sont offertes pour la compilation du
programme. Vous pouvez invoquer le compilateur Java par un appel gnrique :
javac Employee*.java

Tous les fichiers source correspondants seront compils en des fichiers de classe. Ou vous pouvez
simplement taper :
javac EmployeeTest.java

Il peut vous paratre surprenant que la seconde possibilit fonctionne, puisque le fichier
Employee.java nest jamais explicitement compil. Pourtant, lorsque le compilateur Java verra que
la classe Employee est utilise dans EmployeeTest.java, il recherchera un fichier
Employee.class. Sil ne le trouve pas, il recherchera automatiquement Employee.java et le
compilera. Mieux encore, si la date de la version de Employee.java quil trouve est plus rcente que
celle existant dans le fichier Employee.class, le compilateur Java recompilera automatiquement le
fichier.
INFO
Si vous tes habitu la fonctionnalit make dUNIX (ou lun de ses cousins Windows comme nmake), vous pouvez
imaginer le compilateur Java comme possdant la fonctionnalit make intgre.

Analyser la classe Employee


Nous allons dissquer la classe Employee dans les sections qui suivent. Commenons par les mthodes.
Comme vous pouvez le voir en examinant le code source, cette classe possde un constructeur et
quatre mthodes :
public
public
public
public
public

Employee(String n, double s, int year, int month, int day)


String getName()
double getSalary()
Date getHireDay()
void raiseSalary(double byPercent)

Toutes les mthodes de cette classe sont publiques. Le mot cl public signifie que les mthodes
peuvent tre appeles par nimporte quelle mthode de nimporte quelle classe. Il existe quatre
niveaux daccs (ou niveaux de visibilit), qui seront dcrits dans une prochaine section ainsi quau
chapitre suivant.
Remarquez galement que trois champs dinstance contiendront les donnes que nous manipulerons
dans une instance de la classe Employee:
private String name;
private double salary;
private Date hireDay;

Livre Java .book Page 131 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

131

Le mot cl private (priv) assure que les seules mthodes pouvant accder ces champs dinstance
sont les mthodes de la classe Employee elle-mme. Aucune mthode externe ne peut lire ou crire
dans ces champs.
INFO
Il est possible demployer le mot cl public avec vos champs dinstance, mais ce serait une trs mauvaise ide. Si des
champs de donnes sont publics, les champs dinstance peuvent tre lus et modifis par nimporte quelle partie du
programme. Une telle situation irait compltement lencontre du principe dencapsulation. Toute mthode de
toute classe peut modifier les champs publics (et, notre avis, certaines parties de code profiteront de ce privilge
daccs au moment o vous vous y attendrez le moins). Nous insistons sur le fait que vos champs dinstance doivent
tre privs.

Notez encore que deux des champs dinstance sont eux-mmes des objets. Les champs name et
hireDay sont des rfrences des objets String et Date. Il sagit l dune situation courante : les
classes contiennent souvent des champs dinstance du type classe.

Premiers pas avec les constructeurs


Examinons le constructeur de la classe Employee:
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}

Vous constatez que le nom du constructeur est le mme que le nom de la classe. Ce constructeur
sexcute lorsque vous construisez des objets de la classe Employee, et il attribue aux champs
dinstance ltat initial que vous voulez leur donner.
Par exemple, si vous crez une instance de la classe Employee avec des instructions de ce genre :
new Employee("James Bond", 100000, 1950, 1, 1);

les champs dinstance sont affects de la manire suivante :


name = "James Bond";
salary = 100000;
hireDay = January 1, 1950;

Il existe une diffrence importante entre les constructeurs et les autres mthodes. Un constructeur
peut seulement tre appel en association avec loprateur new. Vous ne pouvez pas appliquer un
constructeur un objet existant pour redfinir les champs dinstance. Par exemple,
james.Employee("James Bond", 250000, 1950, 1, 1); // ERREUR

provoquera une erreur de compilation.


Nous reparlerons des constructeurs, mais gardez toujours lesprit les points suivants :
m

Un constructeur porte le mme nom que la classe.

Une classe peut avoir plus dun constructeur.

Livre Java .book Page 132 Jeudi, 25. novembre 2004 3:04 15

132

Au cur de Java 2 - Notions fondamentales

Un constructeur peut avoir un ou plusieurs paramtres, ou ventuellement aucun.

Un constructeur ne renvoie aucune valeur.

Un constructeur est toujours appel laide de loprateur new.


INFO C++

Les constructeurs fonctionnent de la mme manire en Java et en C++. Mais souvenez-vous que tous les objets Java
sont construits dans la mmoire heap et quun constructeur doit tre combin avec new. Les programmeurs C++
oublient facilement loprateur new:
Employee number007("James Bond", 100000, 1950, 11);
// OK en C++, incorrect en Java.

Cette instruction fonctionne en C++, mais pas en Java.

ATTENTION
Prenez soin de ne pas dclarer des variables locales ayant le mme nom que des champs dinstance. Par exemple, le
constructeur suivant ninitialisera pas le salaire (salary) :
public Employee(String n, double s, . . .)
{
String name = n; // ERREUR
double salary = s; // ERREUR
. . .
}

Le constructeur dclare les variables locales name et salary. Ces variables ne sont accessibles que dans le constructeur. Elles clipsent les champs dinstance de mme nom. Certains programmeurs comme les auteurs de ce livre
crivent ce type de code sils tapent plus vite quils ne pensent, car leurs doigts ont lhabitude dajouter le type de
donnes. Cest une erreur sournoise qui peut se rvler difficile dtecter. Il faut donc faire attention, dans toutes
les mthodes, ne pas utiliser des variables qui soient homonymes des champs dinstance.

Paramtres implicites et explicites


Les mthodes agissent sur les objets et accdent leurs champs dinstance.
Par exemple, la mthode
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}

affecte une nouvelle valeur au champ dinstance salary de lobjet qui excute cette mthode (celleci, en loccurrence, ne renvoie aucune valeur). Ainsi, linstruction
number007.raiseSalary(5);

accrot le salaire de number007 en augmentant la variable number007.salary de 5 %. Plus prcisment,


lappel excute les instructions suivantes :
double raise = number007.salary * 5 / 100;
number007.salary += raise;

Livre Java .book Page 133 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

133

La mthode raiseSalary a deux paramtres. Le premier, appel paramtre implicite, est lobjet de
type Employee qui apparat devant le nom de la mthode lors dun appel. Le second, situ entre
parenthses derrire le nom de la mthode, est un paramtre explicite.
Comme vous pouvez le voir, les paramtres explicites sont spcifis dans la dclaration de la
mthode. Par exemple, double byPercent est explicitement dclar. Le paramtre implicite
napparat pas dans la dclaration de la mthode.
Dans chaque mthode, le mot cl this fait rfrence au paramtre implicite. Si vous prfrez, vous
pouvez crire la mthode raiseSalary de la faon suivante :
public void raiseSalary(double byPercent)
{
double raise = this.salary * byPercent / 100;
this.salary += raise;
}

Certains programmeurs prfrent ce style dcriture, car il fait clairement la distinction entre les
champs dinstance et les variables locales.
INFO C++
En C++, vous dfinissez gnralement les mthodes en dehors de la classe :
void Employee::raiseSalary(double byPercent) // en C++, pas en Java
{
. . .
}

Si vous dfinissez une mthode au sein dune classe, ce sera automatiquement une mthode en ligne :
class Employee
{
. . .
int getName() { return name; } // en ligne en C++
}

Dans le langage Java, toutes les mthodes sont dfinies dans la classe elle-mme. Elles ne sont pas pour autant des
mthodes en ligne. Cest la responsabilit de la machine virtuelle Java de trouver des opportunits pour le remplacement en ligne. Le compilateur en "juste--temps" surveille les appels de mthodes courtes, souvent appeles, mais
pas crases, puis les optimise.

Avantages de lencapsulation
Examinons plus attentivement les mthodes, assez simples, getName, getSalary et getHireDay:
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}

Livre Java .book Page 134 Jeudi, 25. novembre 2004 3:04 15

134

Au cur de Java 2 - Notions fondamentales

Ce sont des exemples manifestes de mthodes daccs. Comme elles renvoient simplement les
valeurs des champs dinstance, elles sont appeles parfois mthodes daccs au champ.
Ne serait-il pas plus simple de rendre les champs name, salary et hireDay publics, au lieu davoir
des mthodes daccs spares ?
Il est important de se rappeler que le champ name est "en lecture seule". Une fois quil est dfini dans
le constructeur, aucune mthode ne peut le modifier. Nous savons donc que ce champ ne peut jamais
tre corrompu.
Le champ salary nest pas en lecture seule, mais il ne peut tre modifi que par la mthode raiseSalary. En particulier, si la valeur du champ se rvlait incorrecte, seule cette mthode devrait tre
dbogue. Si le champ salary avait t public, le responsable de la corruption de cette valeur pourrait
tre nimporte o.
Il peut arriver que vous vouliez lire ou modifier la valeur dun champ dinstance ; vous devez alors
fournir trois lments :
m

un champ de donnes priv ;

une mthode publique daccs ce champ ;

une mthode publique daltration de ce champ.

Le dveloppement de ces lments exige plus de travail que la cration dun simple champ public,
mais les bnfices sont considrables :
1. Il est possible de modifier limplmentation interne sans affecter aucun autre code que celui des
mthodes de la classe.
Par exemple, si le stockage du nom devient :
String firstName;
String lastName;

la mthode getName peut tre modifie pour renvoyer :


firstName+" "+lastName

Cette modification est totalement invisible pour le reste du programme.


Bien entendu, les mthodes daccs et daltration peuvent parfois exiger un gros travail et une
conversion entre les anciennes et les nouvelles donnes. Mais cela nous amne au second avantage
de cette technique.
2. Les mthodes daltration peuvent dtecter des erreurs, ce que ne peuvent pas faire de simples
instructions daffectation.
Par exemple, une mthode setSalary peut sassurer que le salaire nest jamais infrieur 0.
ATTENTION
Prenez soin de ne pas crire des mthodes daccs qui renvoient des rfrences des objets altrables. Nous avons
viol cette rgle dans notre classe Employee, dans laquelle la mthode gethireDay renvoie un objet de la classe
Date:
class Employee
{

Livre Java .book Page 135 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

135

. . .
public Date getHireDay()
{
return hireDay;
}
. . .
private Date hireDay;
}

Le principe dencapsulation est viol ! Considrons le code suivant :


Employee harry = . . .;
Date d = harry.getHireDay();
double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 * 1000;
d.setTime(d.getTime() (long) tenYearsInMilliSeconds);
// ajoutons dix ans danciennet Harry

La cause du problme est subtile. d et harry.hireDay font rfrence au mme objet (voir Figure 4.5). Lapplication
de mthodes daltration d change automatiquement ltat priv de lobjet employee!
Si vous devez renvoyer une rfrence un objet altrable, vous devez dabord le cloner. Un clone est une copie
conforme dun objet et cette copie est stocke un emplacement diffrent de celui de loriginal. Nous tudierons le
clonage plus en dtail au Chapitre 6. Voici le code correct :
class Employee
{
. . .
public Date getHireDay()
{
return (Date)hireDay.clone();
}
. . .
}

En rsum, souvenez-vous quil faut toujours utiliser clone lorsque vous devez retourner une copie dun champ de
donnes altrable.

Figure 4.5

Renvoi d'une rfrence


Figure4.5
un champ altrable.

Renvoi d'une
rfrence un
champ altrable.

harry =
d=

Employee
name =
salary =
hireDay =

Date

Livre Java .book Page 136 Jeudi, 25. novembre 2004 3:04 15

136

Au cur de Java 2 - Notions fondamentales

Privilges daccs fonds sur les classes


Vous savez quune mthode peut accder aux donnes prives de lobjet par lequel elle est invoque.
Certaines personnes trouvent surprenant quune mthode puisse accder aux donnes prives de tous
les objets de sa classe. Examinons par exemple une mthode equals qui compare deux employs :
class Employee
{
. . .
boolean equals(Employee other)
{
return name.equals(other.name);
}
}

Voici un appel typique :


if (harry.equals(boss)) . . .

Cette mthode accde aux champs privs de harry, ce qui nest pas surprenant. Elle accde galement aux champs privs de boss. Cest une opration parfaitement lgale, car boss est un objet
de type Employee, et une mthode de la classe Employee a un droit daccs aux champs privs de
nimporte quel objet de type Employee.
INFO C++
La mme rgle existe en C++. Une mthode peut accder aux caractristiques prives de nimporte quel objet de sa
classe, et pas seulement celles du paramtre implicite.

Mthodes prives
Lorsque nous implmentons une classe, nous spcifions que la visibilit de tous les champs de donnes
est prive, car les donnes publiques sont dutilisation risque. Mais quen est-il des mthodes ? Bien
que la plupart des mthodes soient dclares public, on rencontre frquemment des mthodes
private. Vous voudrez parfois dcomposer le code pour calculer diverses mthodes spares. Gnralement, ces mthodes daide (helper) ne doivent pas faire partie de linterface publique : elles peuvent
tre trop proches de limplmentation actuelle, exiger un protocole spcial ou un type dappel particulier.
Il est prfrable dimplmenter ces mthodes comme prives.
Pour implmenter une mthode prive en Java, il suffit de remplacer le mot cl public par private.
En rendant une mthode prive, nous ne sommes plus tenus de la conserver si nous modifions
limplmentation de la classe. Si la reprsentation interne des donnes est modifie, cette mthode
pourrait se rvler plus difficile implmenter ou devenir inutile. Peu importe : tant que la mthode
est prive, les concepteurs de la classe savent quelle nest jamais employe lextrieur de la classe
et quelle peut donc tre supprime. En revanche, si la mthode est publique, nous ne pouvons pas
simplement labandonner, car un autre code peut lutiliser.

Champs dinstance final


Vous pouvez dfinir un champ dinstance comme final. Un tel champ doit tre initialis lorsque
lobjet est construit. Cest--dire quil doit tre certain que la valeur du champ est dfinie aprs la fin

Livre Java .book Page 137 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

137

de chaque constructeur. Ensuite il ne peut plus tre modifi. Par exemple, un champ name de la classe
Employee peut tre dclar comme final puisquil ne change jamais aprs la construction de
lobjet. Il ny a pas de mthode setName:
class Employee
{
. . .
private final String name;
}

Le modificateur final est particulirement utile pour les champs de type primitif ou pour une classe
inaltrable (une classe est dite inaltrable lorsque aucune de ses mthodes ne modifie ses objets. Par
exemple, la classe String est inaltrable). Pour les classes modifiables, le modificateur final
risque de jeter la confusion dans lesprit du lecteur. Par exemple,
private final Date hiredate;

signifie simplement que la rfrence dobjet stocke dans la variable hiredate nest pas modifie
aprs la construction de lobjet. Cela ne signifie pas pour autant que lobjet est constant. Toute
mthode est libre dappeler la mthode daltration setTime sur lobjet auquel fait rfrence hiredate.

Champs et mthodes statiques


Dans tous les exemples de programmes que vous avez vus, la mthode main est qualifie de static.
Nous allons maintenant tudier la signification de ce modificateur.

Champs statiques
Si vous dfinissez un champ comme static, il ne peut en exister quun seul par classe. En revanche,
chaque objet a sa propre copie de tous les champs dinstance. Supposons, par exemple, que nous
voulions affecter un numro unique didentification chaque employ. Nous ajoutons un champ
dinstance id et un champ statique nextId la classe Employee:
class Employee
{
. . .
private int id;
private static int nextId = 1;
}

Maintenant, chaque objet employ possde son propre champ id, mais un seul champ nextId est
partag entre toutes les instances de la classe. On peut aussi dire quil y a peu prs un millier
dobjets de la classe Employee, et par consquent mille champs dinstance id, un pour chaque objet.
Mais il ny a quun seul champ statique nextId. Mme sil ny a aucun objet Employee, le champ
statique nextId est prsent. Il appartient la classe, pas un objet individuel.
INFO
Dans la plupart des langages de programmation oriente objet, les champs statiques sont appels champs de la
classe. Le terme "static" est un reliquat sans signification de C++.

Livre Java .book Page 138 Jeudi, 25. novembre 2004 3:04 15

138

Au cur de Java 2 - Notions fondamentales

Implmentons une mthode simple :


public void setId()
{
id = nextId;
nextId++;
}

Supposons que vous dfinissiez le numro didentification demploy pour harry:


harry.setId();

Le champ id de harry est ensuite dfini, et la valeur du champ statique nextId est incrmente :
harry.id = . . .;
Employee.nextId++;

Constantes
Les variables statiques sont plutt rares, mais les constantes statiques sont plus courantes. Par exemple,
la classe Math dfinit une constante statique :
public class Math
{
. . .
public static final double PI = 3.14159265358979323846;
. . .
}

Vous pouvez accder cette constante dans vos programmes laide de Math.PI.
Si le mot cl static avait t omis, PI aurait t un champ dinstance de la classe Math. Vous auriez
d avoir recours un objet de la classe Math pour accder PI, et chaque objet Math aurait eu sa
propre copie de PI.
Une autre constante statique que vous avez souvent utilise est System.out. Elle est dclare dans
la classe System:
public class System
{
. . .
public static final PrintStream out = . . .;
. . .
}

Comme nous lavons dj mentionn, il nest jamais souhaitable davoir des champs publics, car
tout le monde peut les modifier. Toutefois, les constantes publiques (cest--dire les champs final)
conviennent. Puisque out a t dclar comme final, vous ne pouvez pas lui raffecter un autre flux
dimpression :
System.out = new PrintStream(. . .); // ERREUR--out est final

INFO
Si vous examinez la classe System, vous remarquerez une mthode setOut qui vous permet daffecter System.out
un flux diffrent. Vous vous demandez peut-tre comment cette mthode peut changer la valeur dune variable
final. Quoi quil en soit, setOut est une mthode native, non implmente dans le langage de programmation
Java. Les mthodes natives peuvent passer outre les mcanismes de contrle daccs de Java. Cest un moyen trs
inhabituel de contourner ce problme, et vous ne devez pas lmuler dans vos propres programmes.

Livre Java .book Page 139 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

139

Mthodes statiques
Les mthodes statiques sont des mthodes qui noprent pas sur les objets. Par exemple, la mthode
pow de la classe Math est une mthode statique. Lexpression
Math.pow(x, a)

calcule la puissance xa. Elle nutilise aucun objet Math pour raliser sa tche. Autrement dit, elle na
pas de paramtre implicite.
Vous pouvez imaginer les mthodes statiques comme des mthodes nayant pas de paramtre this
(dans une mthode non statique, le paramtre this fait rfrence au paramtre implicite de la
mthode, voir prcdemment).
Puisque les mthodes statiques noprent pas sur les objets, vous ne pouvez pas accder aux champs
dinstance partir dune mthode statique. Cependant, les mthodes statiques peuvent accder aux
champs statiques dans leur classe. En voici un exemple :
public static int getNextId()
{
return nextId; // renvoie un champ statique
}

Pour appeler cette mthode, vous fournissez le nom de la classe :


int n = Employee.getNextId();

Auriez-vous pu omettre le mot cl static pour cette mthode ? Oui, mais vous auriez alors d avoir
une rfrence dobjet du type Employee pour invoquer la mthode.
INFO
Il est lgal dutiliser un objet pour appeler une mthode statique. Par exemple, si harry est un objet Employee, vous
pouvez appeler harry.getNextId() au lieu de Employee.getnextId(). Cette criture peut prter confusion.
La mthode getNextId ne consulte pas du tout harry pour calculer le rsultat. Nous vous recommandons dutiliser
les noms de classes, et non les objets, pour invoquer des mthodes statiques.

Vous utilisez les mthodes statiques dans deux cas :


1. Si une mthode na pas besoin daccder ltat de lobjet, car tous les paramtres ncessaires
sont fournis comme paramtres explicites (par exemple, Math.pow).
2. Si une mthode na besoin daccder qu des champs statiques de la classe (par exemple :
Employee.getNextId).
INFO C++
Les champs et mthodes statiques ont la mme fonctionnalit en Java et en C++. La syntaxe est toutefois lgrement
diffrente. En C++, vous utilisez loprateur "::" pour accder un champ ou une mthode statique hors de sa
porte, par exemple Math::PI.
Lhistorique du terme "static" est curieux. Le mot cl static a t introduit dabord en C pour indiquer des variables
locales qui ne disparaissaient pas lors de la sortie dun bloc. Dans ce contexte, le terme "static" est logique : la variable reste et elle est toujours l lors dune nouvelle entre dans le bloc. Puis static a eu une autre signification en
C, il dsignait des variables et fonctions globales non accessibles partir dautres fichiers. Le mot cl static a t

Livre Java .book Page 140 Jeudi, 25. novembre 2004 3:04 15

140

Au cur de Java 2 - Notions fondamentales

simplement rutilis pour viter den introduire un nouveau. Enfin, C++ a repris le mot cl avec une troisime interprtation, sans aucun rapport, pour indiquer des variables et fonctions appartenant une classe, mais pas un objet
particulier de la classe. Cest cette mme signification qua ce terme en Java.

Mthodes "factory"
Voici une autre utilisation courante des mthodes statiques. La classe NumberFormat utilise les
mthodes factory, qui produisent des objets de formatage pour divers styles :
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
double x = 0.1;
System.out.println(currencyFormatter.format(x)); //affiche $0.10
System.out.println(percentFormatter.format(x)); //affiche 10%

Pourquoi ne pas utiliser plutt un constructeur ? Pour deux raisons.


m

Vous ne pouvez pas donner de nom aux constructeurs, le nom dun constructeur est toujours
celui de la classe. Mais nous avons besoin de deux noms diffrents pour obtenir linstance
currency et linstance percent.

Lorsque vous utilisez un constructeur, vous ne pouvez pas modifier le type de lobjet construit.
Mais la mthode factory peut renvoyer un objet du type DecimalFormat ou une sous-classe qui
hrite de NumberFormat (voir le Chapitre 5 pour plus de dtails sur lhritage).

La mthode main
Notez que vous pouvez appeler des mthodes statiques sans avoir aucun objet. Par exemple, vous ne
construisez jamais aucun objet de la classe Math pour appeler Math.pow.
Pour la mme raison, la mthode main est une mthode statique :
public class Application
{
public static void main(String[] args)
{
// construire les objets ici
. . .
}
}

La mthode main nopre sur aucun objet. En fait, lorsquun programme dmarre, il nexiste encore
aucun objet. La mthode statique main sexcute et construit les objets dont le programme a besoin.
ASTUCE
Chaque classe peut avoir une mthode main. Cest une astuce pratique pour le test unitaire de classes. Vous pouvez
par exemple ajouter une mthode main la classe Employee:
class Employee
{
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;

Livre Java .book Page 141 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

141

GregorianCalendar calendar
= new GregorianCalendar(year, month 1, day);
hireDay = calendar.getTime();
}
. . .
public static void main(String[] args) // test unitaire
{
Employee e = new Employee("Romeo", 50000, 2003, 3, 31);
e.raiseSalary(10);
System.out.println(e.getName()+" "+e.getSalary());
}
. . .
}

Si vous voulez tester isolment la classe Employee, vous excutez simplement :


java Employee

Si la classe Employee fait partie dune plus grande application, vous dmarrez lapplication avec :
java Application

et la mthode main de la classe Employee ne sexcute jamais.

Le programme de lExemple 4.3 contient une version simple de la classe Employee avec un champ
statique nextId et une mthode statique getNextId. Un tableau est rempli avec trois objets
Employee et les informations concernant lemploy sont affiches. Enfin, nous affichons les numros
didentification attribus.
Notez que la classe Employee a aussi une mthode main statique pour le test unitaire. Essayez de
lancer les deux :
java Employee

et
java StaticTest

pour excuter les deux mthodes main.


Exemple 4.3 : StaticTest.java
public class StaticTest
{
public static void main(String[] args)
{
// remplir le tableau staff avec 3 objets Employee
Employee[] staff = new Employee[3];
staff[0] = new Employee("Tom", 40000);
staff[1] = new Employee("Dick", 60000);
staff[2] = new Employee("Harry", 65000);
// imprimer les informations concernant
// tous les objets Employee
for (Employee e: staff)

Livre Java .book Page 142 Jeudi, 25. novembre 2004 3:04 15

142

Au cur de Java 2 - Notions fondamentales

{
e.setId();
System.out.println("name="+e.getName()
+",id="+e.getId()
+",salary="+e.getSalary());
}
int n = Employee.getNextId(); // appel mthode statique
System.out.println("Next available id="+n);
}
}
class Employee
{
public Employee(String n, double s)
{
name = n;
salary = s;
id = 0;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public int getId()
{
return id;
}
public void setId()
{
id = nextId; // dfinir id prochain id disponible
nextId++;
}
public static int getNextId()
{
return nextId; // renvoie un champ statique
}
public static void main(String[] args) // test unitaire
{
Employee e = new Employee("Harry", 50000);
System.out.println(e.getName()+" "+e.getSalary());
}

Livre Java .book Page 143 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

private
private
private
private

Objets et classes

143

String name;
double salary;
int id;
static int nextId = 1;

Paramtres des mthodes


Revoyons les termes qui dcrivent comment les paramtres peuvent tre passs une mthode
(ou une fonction) dans un langage de programmation. Le terme appel par valeur signifie que la
mthode rcupre exactement la valeur fournie par lappelant. En revanche, un appel par rfrence signifie que la mthode obtient lemplacement de la variable fournie par lappelant. Une
mthode peut donc modifier la valeur stocke dans une variable passe par rfrence, mais pas
celle dune variable passe par valeur. Ces termes "appels par..." sont standard en informatique et
dcrivent le comportement des paramtres des mthodes dans les diffrents langages de
programmation, pas seulement en Java (en fait, il existe aussi un appel par nom, dont le principal
intrt est historique ; il tait employ avec le langage Algol, lun des plus anciens langages de
haut niveau).
Le langage Java utilise toujours lappel par valeur. Cela signifie que la mthode obtient une copie de
toutes les valeurs de paramtre. En particulier, la mthode ne peut modifier le contenu daucun des
paramtres qui lui sont passs.
Par exemple, dans lappel suivant :
double pourcent = 10;
harry.raiseSalary(percent);

Peu importe comment la mthode est implmente, nous savons quaprs lappel la mthode, la
valeur de percent sera toujours 10.
Examinons cette situation dun peu plus prs. Supposons quune mthode tente de tripler la valeur
dun paramtre de mthode :
public static void tripleValue(double x) // ne marche pas
{
x = 3 * x;
}

Appelons cette mthode :


double percent = 10;
tripleValue(percent);

Cela ne marche pas. Aprs lappel la mthode, la valeur de percent est toujours 10. Voici ce qui se
passe :
1. x est initialis avec une copie de la valeur de percent (cest--dire 10).
2. x est tripl : il vaut maintenant 30. Mais percent vaut toujours 10 (voir Figure 4.6).

Livre Java .book Page 144 Jeudi, 25. novembre 2004 3:04 15

144

Au cur de Java 2 - Notions fondamentales

3. La mthode se termine et la variable paramtre x nest plus utilise.


Figure 4.6
La modification dun
paramtre numrique
na pas deffet durable.

Valeur copie

percent =

10

x=

30

Valeur triple

Il existe pourtant deux sortes de paramtres de mthode :


m

les types primitifs (nombres, valeurs boolennes) ;

les rfrences des objets.

Vous avez vu quil tait impossible pour une mthode de modifier un paramtre de type primitif. La
situation est diffrente pour les paramtres objet. Vous pouvez facilement implmenter une mthode
qui triple le salaire dun employ :
public static void tripleSalary(Employee x) // a marche
{
x.raiseSalary(200);
}

Lorsque vous appelez


harry = new Employee(. . .);
tripleSalary(harry);

voici ce qui se passe :


1. x est initialis avec une copie de la valeur de harry, cest--dire une rfrence dobjet.
2. La mthode raiseSalary est applique cette rfrence dobjet. Lobjet Employee, auquel font
rfrence la fois x et harry, voit son salaire augment de 200 %.
3. La mthode se termine et la variable paramtre x nest plus utilise. Bien entendu, la variable
objet harry continue faire rfrence lobjet dont le salaire a tripl (voir Figure 4.7).

Livre Java .book Page 145 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

145

Figure 4.7
La modification
dun paramtre objet
a un effet durable.

Rfrence
copie

harry =

Salaire tripl

Employee

x=

Vous avez pu voir quil tait facile et en fait trs courant dimplmenter des mthodes qui
changent ltat dun paramtre objet. La raison en est simple. La mthode obtient une copie de la
rfrence dobjet, et la fois loriginal et la copie font rfrence au mme objet.
De nombreux langages de programmation (en particulier, C++ et Pascal) disposent de deux mthodes pour le passage de paramtre : lappel par valeur et lappel par rfrence. Certains programmeurs (et malheureusement aussi certains auteurs de livres) affirment que le langage Java a recours
aux appels par rfrence pour les objets. Cest pourtant une erreur. Elle est dailleurs si frquente que
cela vaut la peine de prendre le temps dtudier un contre-exemple en dtail.
Essayons dcrire une mthode qui change deux objets Employee:
public static void swap(Employee x, Employee y) // ne marche pas
{
Employee temp = x;
x = y;
y = temp;
}

Si le langage Java utilisait lappel par rfrence pour les objets, cette mthode fonctionnerait :
Employee a = new Employee("Alice", . . .);
Employee b = new Employee("Bob", . . .);
swap(a, b);
// est-ce que a fait maintenant rfrence Bob, et b Alice?

Cependant, la mthode ne modifie pas rellement les rfrences dobjet qui sont stockes dans les
variables a et b. Les paramtres x et y de la mthode swap sont initialiss avec les copies de ces rfrences. La mthode poursuit lchange de ces copies :
// x fait rfrence Alice, y Bob
Employee temp = x;
x = y;
y = temp;
// maintenant x fait rfrence Bob, y Alice

Livre Java .book Page 146 Jeudi, 25. novembre 2004 3:04 15

146

Au cur de Java 2 - Notions fondamentales

Mais, en fin de compte, cest une perte de temps. Lorsque la mthode se termine, les variables paramtres x et y sont abandonnes. Les variables originales a et b font toujours rfrence aux mmes
objets, comme avant lappel la mthode (voir Figure 4.8).
Figure 4.8
Lchange de paramtres
objet na pas deffet
durable.

Rfrences
copies
Employee

alice =
bob =
x=

Employee

y=

Rfrences
changes

Cet exemple montre que le langage Java nutilise pas lappel par rfrence pour les objets. Les rfrences dobjet sont passes par valeur.
Voici un rcapitulatif de ce que vous pouvez faire et ne pas faire, avec les paramtres dune mthode,
en langage Java :
m

Une mthode ne peut pas modifier un paramtre de type primitif (cest--dire des nombres ou
des valeurs boolennes).

Une mthode peut modifier ltat dun paramtre objet.

Une mthode ne peut pas modifier un paramtre objet pour quil fasse rfrence un nouvel
objet.

Le programme de lExemple 4.4 en fait la dmonstration. Il essaie dabord de tripler la valeur dun
paramtre numrique et ny parvient pas :
Testing tripleValue:
Before: percent=10.0
End of method: x=30.0
After: percent=10.0

Il parvient ensuite tripler le salaire dun employ :


Testing tripleSalary:
Before: salary=50000.0
End of method: salary=150000.0
After: salary=150000.0

Livre Java .book Page 147 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

147

Aprs appel la mthode, ltat de lobjet auquel harry fait rfrence a chang. Cela est possible,
car la mthode en a modifi ltat par lintermdiaire dune copie de la rfrence dobjet.
Enfin, le programme met en vidence lchec de la mthode swap:
Testing swap:
Before: a=Alice
Before: b=Bob
End of method: x=Bob
End of method: y=Alice
After: a=Alice
After: b=Bob

Vous pouvez voir que les variables paramtres x et y sont changes, mais que les variables a et b ne
sont pas affectes.
INFO C++
C++ ralise des appels la fois par valeur et par rfrence. Les paramtres par rfrence sont baliss par &. Par exemple,
vous pouvez facilement implmenter des mthodes
void tripleValue(double& x)

ou
void swap(Employee& x, Employee& y)

qui modifient leurs paramtres de rfrence.

Exemple 4.4 : ParamTest.java


public class ParamTest
{
public static void main(String[] args)
{
/*
Test 1: les mthodes ne peuvent pas modifier
des paramtres numriques
*/
System.out.println("Testing tripleValue:");
double percent = 10;
System.out.println("Before: percent="+percent);
tripleValue(percent);
System.out.println("After: percent="+percent);
/*
Test 2: les mthodes peuvent changer ltat
des paramtres objets
*/
System.out.println("\nTesting tripleSalary:");
Employee harry = new Employee("Harry", 50000);
System.out.println("Before: salary="+harry.getSalary());
tripleSalary(harry);
System.out.println("After: salary="+harry.getSalary());
/*
Test 3: les mthodes ne peuvent pas attacher de
nouveaux objets aux paramtres objet
*/
System.out.println("\nTesting swap:");

Livre Java .book Page 148 Jeudi, 25. novembre 2004 3:04 15

148

Au cur de Java 2 - Notions fondamentales

Employee a = new Employee("Alice", 70000);


Employee b = new Employee("Bob", 60000);
System.out.println("Before: a="+a.getName());
System.out.println("Before: b="+b.getName());
swap(a, b);
System.out.println("After: a="+a.getName());
System.out.println("After: b="+b.getName());
}
public static void tripleValue(double x) // ne marche pas
{
x = 3 * x;
System.out.println("End of method: x="+x);
}
public static void tripleSalary(Employee x) // a marche
{
x.raiseSalary(200);
System.out.println("End of method: salary="
+x.getSalary());
}
public static void swap(Employee x, Employee y)
{
Employee temp = x;
x = y;
y = temp;
System.out.println("End of method: x="+x.getName());
System.out.println("End of method: y="+y.getName());
}
}
class Employee // classe Employee simplifie
{
public Employee(String n, double s)
{
name = n;
salary = s;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
private String name;
private double salary;
}

Livre Java .book Page 149 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

149

Construction dun objet


Nous avons vu comment crire des constructeurs simples qui dfinissent ltat initial de nos objets.
Cependant, comme la construction dun objet est une opration primordiale, Java offre une grande
diversit de mcanismes permettant dcrire des constructeurs. Nous allons maintenant tudier ces
mcanismes.

Surcharge
Nous avons vu que la classe GregorianCalendar possdait plusieurs constructeurs. Nous pouvons
employer :
GregorianCalendar today = new GregorianCalendar();

ou :
GregorianCalendar deadline
= new GregorianCalendar(2099, Calendar.DECEMBER, 31);

Cette fonctionnalit sappelle surcharge. La surcharge seffectue si plusieurs mthodes possdent le


mme nom (dans ce cas prcis, la mthode du constructeur GregorianCalendar), mais des paramtres diffrents. Le compilateur Java se charge de dterminer laquelle il va employer. Il choisit la
mthode correcte en comparant le type des paramtres des diffrentes dclarations avec celui des
valeurs transmises lors de lappel. Une erreur de compilation se produit si le compilateur se rvle
incapable dapparier les paramtres ou si plusieurs correspondances sont possibles. Ce processus est
appel rsolution de surcharge.
INFO
Java permet de surcharger nimporte quelle mthode pas seulement les constructeurs. Par consquent, pour
dcrire compltement une mthode, vous devez spcifier le nom de la mthode ainsi que les types de ses paramtres.
Cela sappelle la signature de la mthode. Par exemple, la classe String a quatre mthodes appeles indexOf.
Leurs signatures sont :
indexOf(int)
indexOf(int, int)
indexOf(String)
indexOf(String, int)

Le type renvoy ne fait pas partie de la signature de la mthode. Cest--dire que vous ne pouvez pas avoir deux
mthodes avec le mme nom et les mmes types de paramtres, et seulement des types renvoys qui diffrent.

Initialisation des champs par dfaut


Si vous ne dfinissez pas un champ explicitement dans un constructeur, une valeur par dfaut lui est
automatiquement attribue : 0 pour les nombres, false pour les valeurs boolennes, et null pour les
rfrences dobjet. On considre gnralement que ce nest pas une bonne pratique de compter aveuglment sur ce mcanisme. Il est bien videmment plus difficile un tiers de comprendre votre code
si les variables sont initialises de manire invisible.

Livre Java .book Page 150 Jeudi, 25. novembre 2004 3:04 15

150

Au cur de Java 2 - Notions fondamentales

INFO
Il existe une diffrence importante entre les champs et les variables locales. Vous devez toujours explicitement initialiser les variables locales dans une mthode, mais si vous ninitialisez pas un champ dans une classe, il prend automatiquement la valeur par dfaut (0, false ou null).

Prenez, par exemple, la classe Employee. Supposons que vous ne prcisiez pas comment initialiser
certains des champs dans un constructeur. Par dfaut, le champ salary sera initialis 0 et les
champs name et hireDay auront la valeur null.
Cela nest toutefois pas souhaitable, car si quelquun appelle la mthode getName ou getHireDay, il
obtiendra une rfrence null quil nattendait certainement pas :
Date h = harry.getHireDay();
calendar.setTime(h); // lance une exception si h vaut null

Constructeurs par dfaut


Un constructeur par dfaut est un constructeur sans paramtres. Par exemple, voici un constructeur
par dfaut pour la classe Employee:
public Employee()
{
name = "";
salary = 0;
hireDay = new Date();
}

Si vous crivez une classe sans fournir de constructeur, Java en fournit automatiquement un par
dfaut. Ce dernier affecte une valeur par dfaut tous les champs dinstance. Ainsi, toutes les
donnes numriques des champs prennent la valeur 0, tous les boolens reoivent la valeur false et
toutes les variables objet sont initialises avec la valeur null.
Si une classe fournit au moins un constructeur, mais ne fournit pas de constructeur par dfaut, il est
illgal de construire des objets sans paramtres de construction. Par exemple, notre classe dorigine
Employee dans lExemple 4.2 fournissait un unique constructeur :
Employee(String name, double salary, int y, int m, int d)

Il nest pas lgal, avec cette classe, de construire des employs par dfaut. Cest--dire que lappel
e = new Employee();

provoquerait une erreur.


ATTENTION
Retenez bien quun constructeur par dfaut est fourni uniquement si votre classe ne possde pas dautre constructeur. Si vous crivez mme un seul constructeur pour votre classe et que vous vouliez que les utilisateurs de votre
classe puissent en crer une instance par un appel
new NomDeClasse()

Livre Java .book Page 151 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

151

vous devez fournir un constructeur par dfaut explicite (sans arguments). Bien entendu, si les valeurs par dfaut vous
conviennent pour tous les champs, vous pouvez simplement crire :
public NomDeClasse()
{
}

Initialisation explicite de champ


Puisque vous pouvez surcharger les mthodes du constructeur dans une classe, la construction peut
se faire de plusieurs faons pour dfinir ltat initial des champs dinstance de vos classes. Il est
toujours souhaitable de sassurer que, indpendamment de lappel au constructeur, chaque champ
dinstance est dfini une valeur significative.
Vous pouvez simplement affecter une valeur tous les champs dans la dfinition de classe. Par
exemple :
class Employee
{
. . .
private String name = "";
}

Cette affectation est ralise avant lexcution du constructeur. Cette syntaxe est particulirement
utile si tous les constructeurs dune classe ont besoin de dfinir un champ dinstance particulier la
mme valeur.
Linitialisation nest pas ncessairement une valeur constante. Voici un exemple de champ initialis
laide dun appel de mthode. Il sagit dune classe Employee o chaque employ a un champ id.
Vous pouvez linitialiser de la faon suivante :
class Employee
{
. . .
static int assignId()
{
int r = nextId;
nextId++;
return r;
}
. . .
private int id = assignId();
}

INFO C++
En C++, il nest pas possible dinitialiser directement les champs dinstance dune classe. Tous les champs doivent tre
dfinis dans un constructeur. Toutefois, C++ dispose dune syntaxe spciale, la liste dinitialisation :
Employee::Employee(String n, double s,
int y, int m, int d) // C++
: name(n),
salary(s),
hireDay(y, m, d)
{
}

Livre Java .book Page 152 Jeudi, 25. novembre 2004 3:04 15

152

Au cur de Java 2 - Notions fondamentales

C++ utilise cette syntaxe spciale pour appeler des constructeurs de champ. En Java, cest inutile, car les objets nont
pas de sous-objets, seulement des pointeurs sur dautres objets.

Noms de paramtres
Si vous crivez des constructeurs trs triviaux (et vous en crirez beaucoup), la question des noms de
paramtres peut devenir fastidieuse.
Nous avons, en gnral, opt pour des noms de paramtres dun seul caractre :
public Employee(String n, double s)
{
name = n;
salary = s;
}

Linconvnient est quil faut lire le code pour savoir ce que signifient les paramtres n et s.
Certains programmeurs prfixent chaque paramtre avec un "a" :
public Employee(String aName, double aSalary)
{
name = aName;
salary = aSalary;
}

Cest trs clair, nimporte quel lecteur peut immdiatement savoir ce que les paramtres dsignent.
Une autre astuce est couramment utilise. Elle sappuie sur le fait que les variables paramtres clipsent les champs dinstance de mme nom. Si vous appelez un paramtre salary, ce nom salary fait
rfrence au paramtre, et non au champ dinstance. Mais vous pouvez toujours accder au champ
dinstance laide de this.salary. Souvenez-vous que this dsigne le paramtre implicite, cest-dire lobjet qui est en train dtre construit. Voici un exemple :
public Employee(String name, double salary)
{
this.name = name;
this.salary = salary;
}

INFO C++
En C++, il est courant de prfixer les champs dinstance avec un caractre de soulignement (_) ou une lettre fixe. Les
lettres "m" et "x" sont frquemment choisies. Par exemple, le champ salary peut tre appel _salary, mSalary
ou xSalary. Ce nest pas une pratique courante en langage Java.

Appel dun autre constructeur


Le mot cl this fait rfrence au paramtre implicite dune mthode. Il existe cependant une autre
signification pour ce mot cl.
Si la premire instruction dun constructeur a la forme this(...), ce constructeur appelle un autre
constructeur de la mme classe. Voici un exemple caractristique :
public Employee(double s)
{

Livre Java .book Page 153 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

153

// appelle Employee(String, double)


this("Employee #"+nextId, s);
nextId++;
}

Lorsque vous appelez new Employee(60000), le constructeur Employee(double) appelle le


constructeur Employee(String, double).
Cet emploi du mot cl this est pratique, vous navez besoin dcrire le code de construction
commun quune seule fois.
INFO C++
Lobjet this de Java est identique au pointeur this de C++. Nanmoins, en C++, il nest pas possible pour un
constructeur den appeler un autre. Si vous souhaitez partager du code dinitialisation en C++, vous devez crire une
mthode spare.

Blocs dinitialisation
Nous avons dj vu deux faons dinitialiser un champ de donnes :
m

en spcifiant une valeur dans un constructeur ;

en affectant une valeur initiale dans la dclaration.

Il existe en fait un troisime mcanisme, appel bloc dinitialisation. Une dclaration de classe peut
contenir des blocs de code arbitraires qui sont excuts chaque fois quun objet de cette classe est
construit. Par exemple :
class Employee
{
public Employee(String n, double s)
{
name = n;
salary = s;
}
public Employee()
{
name = "";
salary = 0;
}
. . .
private static int nextId;
private int id;
private String name;
private double salary;
...
// bloc dinitialisation dobjet
{
id = nextId;
nextId++;
}
}

Livre Java .book Page 154 Jeudi, 25. novembre 2004 3:04 15

154

Au cur de Java 2 - Notions fondamentales

Dans cet exemple, le champ id est initialis dans le bloc dinitialisation dobjet, peu importe le
constructeur utilis pour construire un objet. Le bloc dinitialisation sexcute en premier, avant
le corps du constructeur.
Ce mcanisme nest jamais ncessaire et il nest pas lgant. Il est gnralement plus clair de placer
le code dinitialisation lintrieur dun constructeur.
INFO
La dfinition des champs dans les blocs dinitialisation est autorise, mme sils ne sont dfinis que plus loin dans la
classe. Certaines versions du compilateur Java de Sun graient incorrectement cette situation (bogue n 4459133). Ce
bogue avait t rsolu dans le JDK 1.4.1. Or, pour viter des dfinitions circulaires, vous ne pouvez pas lire partir de
champs qui ne soient initialiss que par la suite. Les rgles exactes sont numres dans la section 8.3.2.3 des caractristiques du langage Java (http://java.sun.com/docs/books/jls). Ces rgles tant suffisamment complexes pour
tromper limplmenteur, nous vous conseillons de placer les blocs dinitialisation aprs les dfinitions de champs.

Avec toutes ces techniques dinitialisation, il est difficile dindiquer toutes les manires deffectuer
une construction dobjet. Voici en dtail ce qui se passe lorsquun constructeur est appel :
1. Tous les champs de donnes sont initialiss leurs valeurs par dfaut (0, false ou null).
2. Tous les initialiseurs de champ et les blocs dinitialisation sont excuts, dans lordre de leur
apparition lintrieur de la dclaration de classe.
3. Si la premire ligne du constructeur appelle un second constructeur, le corps du second constructeur
est excut.
4. Le corps du constructeur est excut.
Naturellement, il est toujours judicieux dorganiser le code dinitialisation de sorte que lon puisse
aisment le comprendre sans tre un thoricien du langage. Par exemple, il serait curieux et dangereux de crer une classe dont les constructeurs dpendent de lordre dans lequel sont dclars les
champs de donnes.
Un champ statique est initialis, soit en spcifiant une valeur initiale, soit en utilisant un bloc
dinitialisation statique. Vous avez dj vu le premier de ces mcanismes :
static int nextId = 1;

Si les champs statiques de votre classe requirent un code dinitialisation complexe, utilisez un bloc
dinitialisation statique.
Placez le code dans un bloc balis laide du mot cl static. Voici un exemple. Les numros dID
demploys doivent dbuter un nombre entier infrieur 10 000 :
// bloc dinitialisation statique
static
{
Random generator = new Random();
nextId = generator.nextInt(10000);
}

Linitialisation statique est excute au premier chargement de la classe. Comme les champs
dinstance, les champs statiques valent 0, false ou null moins que vous ne les ayez explicitement

Livre Java .book Page 155 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

155

dfinis une autre valeur. Tous les initialiseurs de champs statiques et les blocs dinitialisation statiques
sont excuts dans lordre de leur apparition dans la dclaration de classe.
INFO
Voici une petite fantaisie de Java qui pourra tonner vos collgues programmeurs : il est possible de crer un
programme "Hello, World" en Java sans mme crire une mthode main:
public class Hello
{
static
{
System.out.println("Hello, World");
}
}

Lorsque vous invoquez la classe avec linstruction java Hello, la classe est charge, le bloc dinitialisation statique affiche "Hello, World", et cest seulement ensuite que vous obtenez un affreux message derreur vous signalant que
main nest pas dfinie. Vous pouvez lviter en appelant System.exit(0) la fin du bloc dinitialisation statique.

Le programme de lExemple 4.5 montre plusieurs des fonctionnalits abordes dans cette section :
m

la surcharge de constructeurs ;

lappel dun autre constructeur laide de this(...);

un constructeur par dfaut ;

un bloc dinitialisation dobjet ;

un bloc dinitialisation statique ;

linitialisation de champ dinstance.

Exemple 4.5 : ConstructorTest.java


import java.util.*;
public class ConstructorTest
{
public static void main(String[] args)
{
// remplir le tableau staff avec 3 objets Employee
Employee[] staff = new Employee[3];
staff[0] = new Employee("Harry", 40000);
staff[1] = new Employee(60000);
staff[2] = new Employee();
// afficher les informations concernant
// tous les objets Employee
for (Employee e: staff)
System.out.println("name="+e.getName()
+",id="+e.getId()
+",salary="+e.getSalary());
}
}
class Employee

Livre Java .book Page 156 Jeudi, 25. novembre 2004 3:04 15

156

Au cur de Java 2 - Notions fondamentales

{
// trois constructeurs surchargs
public Employee(String n, double s)
{
name = n;
salary = s;
}
public Employee(double s)
{
// appelle le constructeur Employee(String, double)
this("Employee #"+nextId, s);
}
// le constructeur par dfaut
public Employee()
{
// name initialis ""--voir plus bas
// salary non dfini explicitement--initialis 0
// id initialis dans le bloc dinitialisation
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public int getId()
{
return id;
}
private static int nextId;
private int id;
private String name = ""; // initialisation champ dinstance
private double salary;
// bloc dinitialisation statique
static
{
Random generator = new Random();
// dfinir nextId un nombre alatoire
// entre 0 et 9999
nextId = generator.nextInt(10000);
}
// bloc dinitialisation dobjet
{
id = nextId;
nextId++;
}
}
java.util.Random 1.0

Random()

Construit un nouveau gnrateur de nombres alatoires.

Livre Java .book Page 157 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

157

int nextInt(int n) 1.2

Renvoie un nombre alatoire entre 0 et n-1.

Destruction des objets et mthode finalize


De nombreux langages, tels que C++, disposent de destructeurs explicites permettant daccomplir
les oprations de nettoyage ncessaires la libration de la mmoire alloue aux objets. Puisque
Java, par lintermdiaire du garbage collector (ou ramasse-miettes), effectue un nettoyage automatique, la rcupration manuelle de la mmoire nest pas ncessaire et, par consquent, Java ne prend
pas en charge les destructeurs.
Bien entendu, certains objets utilisent dautres ressources que la mmoire, par exemple un fichier ou
un handle sur un autre objet qui opre sur des ressources systme. Dans ce cas, il est important de
rcuprer et de librer ces ressources lorsquelles ne sont plus utilises.
Java permet dajouter une mthode finalize nimporte quelle classe. Cette mthode sera appele
avant que le ramasse-miettes ne dtruise lobjet. En pratique, ne comptez pas sur la mthode finalize pour rcuprer les ressources qui sont en quantit limite, car vous ne pouvez pas savoir exactement quel moment elle est appele.
INFO
Il existe une mthode System.runFinalizersOnExit(true) pour vous assurer que les mthodes de finalisation
sont appeles avant la fermeture de Java. Cette mthode nest toutefois pas sre et est maintenant dprcie.
A la place, vous pouvez ajouter des "crochets de fermeture" avec la mthode Runtime.addShutdownHook
(voir la documentation API pour en savoir plus).

Si une ressource doit tre libre ds que vous avez fini de lutiliser, vous devez effectuer cette libration manuellement. Ajoutez une mthode dispose que vous appellerez pour nettoyer ce qui doit
ltre. De mme, si vous utilisez une classe qui possde une mthode dispose, appelez cette
mthode ds que vous navez plus besoin de lobjet.

Packages
Java permet de regrouper des classes dans un ensemble (ou un paquet) appel package. Les packages
se rvlent pratiques pour lorganisation de votre travail et pour effectuer une sparation entre vos
crations et les bibliothques fournies par des tiers.
La bibliothque standard de Java est distribue dans un certain nombre de packages, y compris
java.lang, java.util, java.net, etc. Les packages standard de Java constituent des exemples de
packages hirarchiques. Tout comme les rpertoires dun disque dur, les packages peuvent tre organiss suivant plusieurs niveaux dimbrication. Tous les packages standard de Java se trouvent au sein
des hirarchies de package java et javax.
Lutilisation des packages permet de sassurer que le nom de chaque classe est unique. Supposons
que deux programmeurs aient la brillante ide de fournir une classe Employee. Tant quils placent
leurs classes dans des packages diffrents, il ny a pas de conflit. En fait, pour sassurer vraiment que
le nom dun package est unique, Sun recommande dutiliser comme prfixe le nom du domaine

Livre Java .book Page 158 Jeudi, 25. novembre 2004 3:04 15

158

Au cur de Java 2 - Notions fondamentales

Internet de votre socit (a priori unique), crit dans lordre inverse de ses lments. Vous utilisez
ensuite des sous-packages pour les diffrents projets. Par exemple, horstmann.com est un domaine
enregistr par lun des auteurs. Invers, il devient le package com.horstmann. Ce package peut
encore tre subdivis en sous-packages tels que com.horstmann.corejava.
Le seul intrt de limbrication de packages concerne la gestion de noms uniques. Du point de vue
du compilateur, il ny a absolument aucune relation entre les packages imbriqus. Par exemple, les
packages java.util et java.util.jar nont rien voir lun avec lautre. Chacun reprsente sa
propre collection indpendante de classes.

Importation des classes


Une classe peut utiliser toutes les classes de son propre package et toutes les classes publiques des
autres packages.
Vous pouvez accder aux classes publiques dun autre package de deux faons. La premire consiste
simplement ajouter le nom complet du package devant chaque nom de classe. Par exemple :
java.util.Date today = new java.util.Date();

Cest une technique plutt contraignante. Il est plus simple davoir recours import. La directive
import constitue un raccourci permettant de faire rfrence aux classes du package. Une fois que
cette directive est spcifie, il nest plus ncessaire de donner aux classes leur nom complet.
Vous pouvez importer une classe spcifique ou lensemble dun package. Vous placez les instructions import en tte de vos fichiers source (mais au-dessous de toutes les instructions package). Par
exemple, vous pouvez importer toutes les classes du package java.util avec linstruction :
import java.util.*;

Vous pouvez alors crire


Date today = new Date();

sans le prfixe du package. Il est galement possible dimporter une classe spcifique dun package :
import java.util.Date;

La syntaxe java.util.* est moins complique. Cela nentrane aucun effet ngatif sur la taille du
code.Si vous importez explicitement des classes, le lecteur de votre code connat exactement les
classes utilises.
ASTUCE
Dans Eclipse, vous pouvez slectionner loption de menu Source/Organize Imports. Les instructions des packages
comme import java.util.* sont automatiquement tendues en une liste dimportations spcifiques comme :
import java.util.ArrayList;
import java.util.Date;

Cest une fonctionnalit trs commode.

Sachez toutefois que vous ne pouvez utiliser la notation * que pour importer un seul package. Vous
ne pouvez pas utiliser import java.* ni import java.*.* pour importer tous les packages ayant
le prfixe java.

Livre Java .book Page 159 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

159

Le plus souvent, vous importez simplement les packages dont vous avez besoin, sans autre proccupation. La seule circonstance demandant une attention particulire est le conflit de noms. Par
exemple, la fois les packages java.util et java.sql ont une classe Date. Supposons que vous
criviez un programme qui importe les deux packages :
import java.util.*;
import java.sql.*;

Si vous utilisez maintenant la classe Date, vous obtiendrez une erreur de compilation :
Date today; // ERREUR--java.util.Date ou java.sql.Date?

Le compilateur ne peut dterminer de quelle classe Date vous avez besoin. Ce problme peut tre
rsolu par lajout dune instruction import spcifique :
import java.util.*;
import java.sql.*;
import java.util.Date;

Mais si vous avez rellement besoin des deux classes Date? Vous devez alors utiliser le nom
complet du package avec chaque nom de classe :
java.util.Date deadline = new java.util.Date();
java.sql.Date today = new java.sql.Date();

La localisation des classes dans les packages est le rle du compilateur. Les bytecodes dans les
fichiers de classe utilisent toujours les noms complets de packages pour faire rfrence aux autres
classes.
INFO C++
Les programmeurs C++ font souvent une confusion entre import et #include. Ces deux directives nont rien de
commun. En C++, il faut employer #include pour inclure les dclarations des composants externes parce que le
compilateur C++ ne consulte aucun fichier part celui quil compile et les fichiers den-ttes explicitement spcifis.
Le compilateur Java, pour sa part, cherchera dans dautres fichiers condition que vous lui fournissiez une directive
de recherche.
En Java, il est possible dviter compltement le mcanisme import en nommant explicitement tous les packages,
comme java.util.Date. En C++, vous ne pouvez pas viter les directives #include.
La directive import est purement une facilit du langage permettant de se rfrer une classe en lui donnant un
nom plus court que celui complet du package. Par exemple, aprs une instruction import java.util.* (ou
import java.util.Date), vous pouvez faire rfrence la classe java.util.Date en lappelant simplement
Date.
En C++, une construction analogue au package est la fonctionnalit despace de nom (namespace). Songez aux mots
cls package et import de Java comme des quivalents des directives namespace et using de C++.

Imports statiques
Depuis le JDK 5.0, linstruction import a t amliore de manire permettre limportation de
mthodes et de champs statiques, et non plus simplement des classes.
Par exemple, si vous ajoutez la directive
import static java.lang.System.*;

Livre Java .book Page 160 Jeudi, 25. novembre 2004 3:04 15

160

Au cur de Java 2 - Notions fondamentales

en haut de votre fichier source, vous pouvez utiliser les mthodes et les champs statiques de la classe
Systme, sans prfixe du nom de classe :
out.println("Goodbye, World!"); // cest--dire System.out
exit(0); // cest--dire System.exit

Vous pouvez galement importer une mthode ou un champ spcifique :


import static java.lang.System.out;

Dans la pratique, il semble douteux que de nombreux programmeurs souhaitent abrger System.out
ou System.exit. Le rsultat est moins clair. Mais il existe deux utilisations pratiques des imports
statiques.
1. Les fonctions mathmatiques. Si vous utilisez un import statique pour la classe Math, vous
pouvez utiliser des fonctions mathmatiques dune manire naturelle. Par exemple,
sqrt(pow(x, 2) + pow(y, 2))

semble plus clair que


Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))

2. Des constantes encombrantes. Si vous utilisez de nombreuses constantes avec des noms
compliqus, limport statique vous ravira. Par exemple,
if (d.get(DAY_OF_WEEK) == MONDAY)

est plus agrable lil que


if (d.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY)

Ajout dune classe dans un package


Pour placer des classes dans un package, vous devez mettre le nom du package en haut de votre
fichier source, avant le code qui dfinit les classes dans le package. Par exemple, le fichier
Employee.java dans lExemple 4.7 commence ainsi :
package com.horstmann.corejava;
public class Employee
{
. . .
}

Si vous ne mettez pas une instruction package dans le fichier source, les classes dans ce fichier
source appartiendront au package par dfaut qui na pas de nom de package. Jusqu prsent, tous
nos exemples de classes se trouvaient dans le package par dfaut.
Vous placez les fichiers dun package dans un sous-rpertoire correspondant au nom complet du
package. Par exemple, tous les fichiers de classe dans le package com.horstmann.corejava doivent
se trouver dans le sous-rpertoire com/horstmann/corejava (com\horstmann\corejava sous
Windows).
Le programme des Exemples 4.6 et 4.7 est rparti sur deux packages : la classe PackageTest
appartient au package par dfaut et la classe Employee au package com.horstmann.corejava.

Livre Java .book Page 161 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

161

Le fichier Employee.class doit donc se trouver dans un sous-rpertoire com/horstmann/corejava.


En dautres termes, la structure de rpertoire est la suivante :
. (rpertoire courant)
PackageTest.java
PackageTest.class
com/
horstmann/
corejava/
Employee.java
Employee.class

Pour compiler ce programme, positionnez-vous dans le rpertoire de base et lancez la commande :


javac PackageTest.java

Le compilateur recherche automatiquement le fichier com/horstmann/corejava/Employee.java


et le compile.
Etudions un exemple plus raliste, dans lequel nous nutilisons pas le package par dfaut, mais dont
les classes sont distribues sur plusieurs packages (com.horstmann.corejava et com.mycompany) :
. (rpertoire courant)
com/
horstmann/
corejava/
Employee.java
Employee.class
mycompany/
PayrollApp.java
PayrollApp.class

Dans cette situation, vous devez toujours compiler et excuter des classes depuis le rpertoire de
base, cest--dire le rpertoire contenant le rpertoire com:
javac com/mycompany/PayrollApp.java
java com.mycompany.PayrollApp

Noubliez pas que le compilateur fonctionne sur les fichiers (avec les sparateurs de fichiers et une
extension .java), tandis que linterprteur Java charge une classe (avec des sparateurs par point).
ATTENTION
Le compilateur ne vrifie pas les rpertoires lorsquil compile les fichiers source. Supposons, par exemple, que vous
ayez un fichier source commenant par la directive :
package com.mycompany;

Vous pouvez compiler le fichier, mme sil ne se trouve pas dans un sous-rpertoire com/mycompany. La compilation
se droulera sans erreurs, sil ne dpend pas des autres packages. Toutefois, la machine virtuelle ne trouvera pas les
classes rsultantes lorsque vous tenterez dexcuter le programme.

Exemple 4.6 : PackageTest.java


import com.horstmann.corejava.*;
// La classe Employee est dfinie dans ce package
import static java.lang.System.*;
public class PackageTest
{

Livre Java .book Page 162 Jeudi, 25. novembre 2004 3:04 15

162

Au cur de Java 2 - Notions fondamentales

public static void main(String[] args)


{
// du fait de linstruction import, nous nutilisons pas
// com.horstmann.corejava.Employee ici
Employee harry = new Employee("Harry Hacker", 50000,
1989, 10, 1);
// augmenter le salaire de 5%
harry.raiseSalary(5);
// afficher les informations concernant harry
// utiliser java.lang.System.out ici
out.println("name="+harry.getName()
+",salary="+harry.getSalary());
}
}

Exemple 4.7 : Employee.java


package com.horstmann.corejava;
// les classes de ce fichier font partie de ce package
import java.util.*;
// les instructions import viennent aprs linstruction package
public class Employee
{
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
// GregorianCalendar utilise 0 pour janvier
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
private String name;
private double salary;
private Date hireDay;
}

Livre Java .book Page 163 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

163

Comment la machine virtuelle localise les classes


Vous avez vu que les classes taient stockes dans des sous-rpertoires du systme de fichiers. Le
chemin daccs de la classe doit correspondre au nom du package. Vous pouvez aussi utiliser lutilitaire JAR pour ajouter des fichiers de classe un archive. Un archive contient plusieurs fichiers de
classe et des sous-rpertoires dans un seul fichier, ce qui conomise lespace et rduit le temps
daccs (nous tudierons les fichiers JAR plus en dtail au Chapitre 10).
Par exemple, les milliers de classes de la bibliothque dexcution sont toutes contenues dans le fichier
de la bibliothque dexcution rt.jar. Ce fichier se trouve dans le sous-rpertoire jre/lib du JDK.
ASTUCE
Les fichiers JAR ont recours au format ZIP pour lorganisation des fichiers et sous-rpertoires. Vous pouvez utiliser
nimporte quel utilitaire ZIP pour explorer rt.jar et les autres fichiers JAR.

Dans lexemple de programme prcdent, le rpertoire du package com/horstmann/corejava tait


un sous-rpertoire du rpertoire du programme. Cette organisation nest cependant pas trs souple.
Gnralement de nombreux programmes doivent avoir accs aux fichiers de package. Pour rendre
vos packages accessibles aux programmes, vous devez :
1. Placer vos classes lintrieur dun ou plusieurs rpertoires spciaux, disons /home/user/
classdir. Notez que ce rpertoire est celui de base pour larborescence de package. Si vous
ajoutez la classe com.horstmann.corejava.Employee, le fichier de classe doit tre localis
dans le sous-rpertoire /home/user/classdir/com/horstmann/corejava.
2. Dfinir le chemin de classe (classpath). Ce chemin est la collection de tous les rpertoires de
base dont les sous-rpertoires peuvent contenir des fichiers de classe.
La dfinition du chemin de classe dpend de votre environnement de compilation. Si vous utilisez le
JDK, deux choix soffrent vous : spcifier loption -classpath pour le compilateur et linterprteur de bytecode, ou dfinir la variable denvironnement CLASSPATH.
Les dtails dpendent de votre systme dexploitation. Sous UNIX, les lments dans le chemin de
classe sont spars par des caractres deux-points :
/home/user/classes:.:/home/user/archives/archive.jar

Sous Windows, ils sont spars par des points-virgules :


c:\classes;.;c:\archives\archive.jar

Dans les deux cas, le point dsigne le rpertoire courant.


Ce chemin de classe contient :
m

le rpertoire de base /home/user/classdir ou c:\classes;

le rpertoire courant (.) ;

le fichier JAR /home/user/archives/archive.jar ou c:\archives\archive.jar.

Livre Java .book Page 164 Jeudi, 25. novembre 2004 3:04 15

164

Au cur de Java 2 - Notions fondamentales

Les classes sont toujours recherches dans les fichiers de bibliothque dexcution (rt.jar et les
autres fichiers JAR dans les rpertoires jre/lib et jre/lib/ext) ; vous ne les incluez pas explicitement dans le chemin de classe.
INFO
Un changement est intervenu par rapport aux versions 1.0 et 1.1 du kit Java Development Kit. Dans ces versions, les
classes systme taient stockes dans classes.zip qui devait faire partie du chemin daccs aux classes.

Voici, par exemple, la faon de dfinir le chemin daccs aux classes pour le compilateur :
javac -classpath /home/user/classdir:.:/home/user/archives/archive.jar
MyProg.java

(Toutes les instructions doivent tre tapes sur une seule ligne. Sous Windows, utilisez le pointvirgule pour sparer les lments du chemin de classes.)
ASTUCE
Vous pouvez galement utiliser -cp au lieu de -classpath. Toutefois, avant le JDK 5.0, cette option ne fonctionnait quavec linterprteur de bytecode java, et vous deviez utiliser -classpath avec le compilateur
javac.

Le chemin de classe liste tous les rpertoires et fichiers archive qui reprsentent des points de dpart
pour la localisation des classes. Examinons lexemple suivant :
/home/user/classdir:.:/home/user/archives/archive.jar

Supposons que linterprteur recherche le fichier de la classe com.horstmann.corejava.Employee.


Il va dabord rechercher dans les fichiers de classe systme qui sont stocks dans les archives des rpertoires jre/lib et jre/lib/ext. Il ne trouvera pas le fichier de classes l, il va donc se tourner vers le
chemin de classe et rechercher les fichiers suivants :
m

/home/user/classdir/com/horstmann/corejava/Employee.class;

com/horstmann/corejava/Employee.class en commenant par le rpertoire courant ;

com/horstmann/corejava/Employee.class dans /home/user/archives/archive.jar.


INFO

La tche du compilateur est plus difficile que celle de la machine virtuelle en ce qui concerne la localisation de
fichiers. Si vous faites rfrence une classe sans spcifier son package, le compilateur doit dabord trouver le
package qui contient la classe. Il consulte toutes les directives import en tant que sources possibles pour la classe.
Supposons par exemple que le fichier source contienne les directives
import java.util.*;
import com.horstmann.corejava.*;

Livre Java .book Page 165 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

165

et que le code source fasse rfrence une classe Employee. Le compilateur essaiera alors de trouver
java.lang.Employee (car le package java.lang est toujours import par dfaut), java.util.Employee,
com.horstmann.corejava.Employee et Employee dans le package courant. Il recherche chacune de ces classes
dans tous les emplacements du chemin de classe. Une erreur de compilation se produit si plus dune classe est trouve
(les classes devant tre uniques, lordre des instructions import na pas dimportance).
Ltape suivante pour le compilateur consiste consulter les fichiers source pour voir si la source est plus rcente que
le fichier de classe. Si oui, le fichier source est recompil automatiquement. Souvenez-vous que vous ne pouvez
quimporter des classes publiques des autres packages. Un fichier source peut seulement contenir une classe publique, et les noms du fichier et de la classe publique doivent correspondre. Le compilateur peut donc facilement localiser les fichiers source pour les classes publiques. Vous pouvez importer des classes non publiques partir des
packages courants. Ces classes peuvent tre dfinies dans des fichiers source avec des noms diffrents. Si vous importez une classe partir du package courant, le compilateur examine tous les fichiers source de ce package pour vrifier
lequel dfinit la classe.

ATTENTION
Le compilateur javac recherche toujours les fichiers dans le rpertoire courant, mais linterprteur java nexamine
le rpertoire courant que si le rpertoire "." fait partie du chemin daccs de classe. En labsence de dfinition de
chemin de classe, cela ne pose pas de problme, le chemin de classe par dfaut est le rpertoire ".". Mais, si vous
avez dfini le chemin de classe et oubli dinclure le rpertoire ".", vos programmes seront compils sans erreur,
mais ils ne pourront pas sexcuter.

Dfinition du chemin de classe


Comme vous venez de le voir, vous pouvez dfinir le chemin de classe avec loption -classpath
pour les programmes javac et java. Nous prfrons cette option, mais certains programmeurs la
jugent ennuyeuse. Vous pouvez aussi dfinir la variable denvironnement CLASSPATH. Voici quelques
conseils pour dfinir la variable denvironnement CLASSPATH sous UNIX/Linux et Windows.
Sous UNIX/Linux, modifiez le fichier de dmarrage de votre shell.
Si vous utilisez le shell C, ajoutez la ligne suivante au fichier .cshrc de votre rpertoire de base :
setenv CLASSPATH /home/user/classdir:.
Si vous utilisez le shell Bourne Again ou bash, ajoutez la ligne suivante au fichier .bashrc ou
.bash_profile dans votre rpertoire de base :
export CLASSPATH=/home/user/classdir:.
Sous Windows 95/98/Me, modifiez le fichier autoexec.bat dans le lecteur racine (gnralement
C:). Ajoutez la ligne :
SET CLASSPATH=c:\user\classdir;.
Vrifiez de ne pas placer despaces de lun ou de lautre ct du caractre =.
Sous Windows NT/2000/XP, ouvrez le Panneau de configuration. Cliquez ensuite sur licne Systme et choisissez longlet Environnement. Ajoutez une nouvelle variable denvironnement
nomme CLASSPATH ou modifiez la variable si elle existe dj. Dans le champ de valeur, tapez le

Livre Java .book Page 166 Jeudi, 25. novembre 2004 3:04 15

166

Au cur de Java 2 - Notions fondamentales

chemin de classe souhait comme c:\user\classdir;. (voir Figure 4.9).


Figure 4.9
Dfinition du chemin de
classe sous Windows XP.

Visibilit dans un package


Nous avons dj rencontr les modificateurs daccs public et private. Les composants logiciels
dclars public sont utilisables par nimporte quelle classe. Les lments private ne sont accessibles que dans la classe qui les dfinit. Si vous ne spcifiez pas de modificateur public ou private,
un composant (classe, mthode ou variable) est accessible toutes les mthodes du mme package.
Revenons lExemple 4.2. La classe Employee ntait pas dfinie en tant que classe publique.
Par consquent, seules les autres classes de son package en loccurrence, le package par
dfaut, comme EmployeeTest peuvent y accder. Pour les classes, il sagit dune situation
par dfaut assez raisonnable. En revanche, ce fut un choix malheureux en ce qui concerne les
variables. Celles-ci doivent maintenant tre explicitement spcifies private si lon ne souhaite
pas que leur visibilit stende par dfaut tout le package (ce qui serait en contradiction avec la
rgle de lencapsulation). Le problme nat du fait quil est trs facile doublier de taper le mot
cl private. Voici un exemple de la classe Window du package java.awt, qui fait partie du code
source fourni avec le JDK :
public class Window extends Container
{
String warningString;
. . .
}

Notez que la variable warningString nest pas prive ! Cela signifie que les mthodes de toutes les
classes du package java.awt ont accs cette variable et peuvent lui affecter nimporte quelle
chane. En fait, les seules mthodes qui accdent cette variable se trouvent dans la classe Window,
et une dclaration private aurait donc t parfaitement approprie. Nous pouvons supposer que le
programmeur tait press en tapant le code et quil a tout bonnement oubli le modificateur
private.

Livre Java .book Page 167 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

167

INFO
Il est surprenant de constater que ce problme na jamais t corrig, bien que nous layons signal dans sept
ditions de ce livre il semble que les implmenteurs de bibliothque ne lisent pas cet ouvrage. Par ailleurs, de
nouveaux champs ont t ajouts la classe au cours des annes, et environ la moiti dentre eux ne sont pas privs
non plus.

Est-ce rellement un problme ? Cela dpend. Par dfaut, les packages ne sont pas des entits
fermes. Cest--dire que nimporte qui peut y ajouter des lments. Des programmeurs malintentionns peuvent donc ajouter du code qui modifie les variables dont la visibilit stend la totalit
du package. Par exemple, dans des versions prcdentes du langage de programmation Java, il tait
trs facile de pntrer dans une autre classe du package java.awt, simplement en dmarrant la
classe avec
package java.awt;

puis de placer le fichier de classe rsultant dans un sous-rpertoire java\awt quelque part dans le
chemin de classe, et vous pouviez avoir accs aux structures internes du package java.awt. Grce
ce subterfuge, il tait possible de redfinir le message davertissement (voir Figure 4.10).
Figure 4.10
Changement
du message davertissement
dans la fentre dun applet.

A partir de la version 1.2, les concepteurs du JDK ont modifi le chargeur de classe pour explicitement dsactiver le chargement de classes dfinies par lutilisateur, et dont le nom de package
commencerait par "java."! Bien entendu, vos propres classes ne bnficient pas de cette protection. Vous pouvez utiliser la place un autre mcanisme, le package sealing (plombage de package),
qui rsout le problme de laccs trop libre au package. Si vous plombez un package, aucune autre
classe ne peut lui tre ajoute. Vous verrez au Chapitre 10 comment produire un fichier JAR qui
contient des packages plombs.

Commentaires pour la documentation


Le JDK contient un outil trs utile, appel javadoc, qui gnre une documentation au format
HTML partir de vos fichiers source. En fait, la documentation API que nous avons dcrite au
Chapitre 3 est simplement le rsultat de lexcution de javadoc sur le code source de la bibliothque Java standard.

Livre Java .book Page 168 Jeudi, 25. novembre 2004 3:04 15

168

Au cur de Java 2 - Notions fondamentales

Si vous ajoutez des commentaires commenant par le dlimiteur spcial /** votre code source,
vous pouvez gnrer facilement une documentation daspect trs professionnel. Ce systme est astucieux, car il vous permet de conserver votre code et votre documentation au mme endroit. Si votre
documentation se trouve dans un fichier spar, le code et les commentaires divergeront fatalement
un moment donn. Mais puisque les commentaires de documentation sont dans le mme fichier
que le code source, il est facile de les mettre jour en mme temps et dexcuter javadoc.

Insertion des commentaires


Lutilitaire javadoc extrait les informations concernant les lments suivants :
m

les packages ;

les classes publiques et les interfaces ;

les mthodes publiques et protges ;

les champs publics et protgs.

Les fonctionnalits protges seront examines au Chapitre 5 ; les interfaces, au Chapitre 6.


Vous pouvez (et devez) fournir un commentaire pour chacune de ces fonctionnalits. Chaque
commentaire est plac immdiatement au-dessus de la fonctionnalit quil dcrit. Un commentaire
commence par /** et se termine par */.
Chaque squence /** . . . */ contient un texte au format libre suivi par des balises. Une balise
commence par un @, comme par exemple, @author ou @param.
La premire phrase du commentaire au format libre doit tre une instruction de sommaire. Lutilitaire javadoc gnre automatiquement les pages de sommaire qui extraient ces phrases.
Dans le texte libre, vous pouvez utiliser des balises HTML telles que <em>...</em> pour insister,
<code>...</code> pour une police espacement fixe, <strong>...</strong> pour des caractres
gras, et mme <img ...> pour inclure une image. Evitez cependant les balises <h1> ou <hr> qui
peuvent interfrer avec la mise en forme du document.
INFO
Si vos commentaires contiennent des liens vers dautres fichiers comme des images (par exemple des diagrammes ou des images de composants de linterface utilisateur), placez ces fichiers dans des sous-rpertoires appels doc-files. Lutilitaire javadoc copiera ces rpertoires et les fichiers quils contiennent vers le rpertoire
documentation.

Commentaires de classe
Un commentaire de classe doit tre plac aprs les instructions import, juste avant la dfinition
class.
Voici un exemple :
/**
Un objet <code>Card</code> reprsente une carte jouer, telle
que "Dame de coeur". Une carte a une couleur (Pique, Coeur,
Trfle ou Carreau) et une valeur (1 = As, 2 . . . 10, 11 = Valet,
12 = Dame, 13 = Roi).

Livre Java .book Page 169 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

169

*/
public class Card
{
. . .
}

INFO
De nombreux programmeurs commencent chaque ligne de documentation par un astrisque, comme ci-aprs :
/**
* Un objet <code>Card</code> reprsente une carte jouer, telle
* que "Dame de cur". Une carte a une couleur (Pique, Cur,
* Trfle ou Carreau) et une valeur (1 = As, 2 . . . 10, 11 = Valet,
* 12 = Dame, 13 = Roi)
*/

Cette mthode nest pas recommande, car elle dcourage les programmeurs de mettre jour les commentaires. Il
est fastidieux de rorganiser les * si la longueur de la ligne change. Certains diteurs de texte prennent en charge
cette corve. Si vous savez que les futurs mainteneurs de votre code vont utiliser un tel diteur de texte, vous pouvez
ajouter ce style de dtail qui dlimite bien les commentaires.

Commentaires de mthode
Chaque commentaire de mthode doit immdiatement prcder la mthode quil dcrit. En plus des
balises gnrales, vous pouvez utiliser les balises suivantes :
@param description de variable

Cette balise ajoute une entre la section des paramtres (parameters) de la mthode courante. La
description peut occuper plusieurs lignes et avoir recours aux balises HTML. Toutes les balises
@param pour une mthode doivent tre regroupes :
@return description

Cette balise ajoute une section de renvoi (returns) la mthode courante. La description peut occuper
plusieurs lignes et avoir recours aux balises HTML :
@throws description de classe

Cette balise ajoute une note concernant le dclenchement possible dune exception par la mthode.
Les exceptions sont traites au Chapitre 11.
Voici un exemple de commentaire de mthode :
/**
Augmente le salaire dun employ.
@param byPercent le pourcentage daugmentation du salaire
(par ex. 10 = 10%)
@return le montant de laugmentation
*/
public double raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
return raise;
}

Livre Java .book Page 170 Jeudi, 25. novembre 2004 3:04 15

170

Au cur de Java 2 - Notions fondamentales

Commentaires de champ
Seuls les champs publics doivent tre documents cest--dire gnralement des constantes statiques.
Par exemple :
/**
La couleur "Coeur"
*/
public static final int HEARTS = 1;

Commentaires gnraux
Les balises suivantes peuvent tre employes dans les commentaires de documentation de classe :
@author nom

Cette balise cre une mention dauteur (author). Il peut y avoir plusieurs balises @author, une pour
chaque auteur :
@version texte

Cette balise cre une entre "version". Le texte dcrit la version courante.
Les balises suivantes peuvent tre employes dans tous les commentaires de documentation :
@since texte

Cette balise cre une entre "depuis" (since). Le texte peut tre toute description de la version ayant
introduit cette fonctionnalit. Par exemple, @since version1.7.1:
@deprecated texte

Cette balise ajoute un commentaire signalant que la classe, la mthode ou la variable est dprcie et
ne doit plus tre utilise. Le texte doit suggrer une solution de remplacement. Par exemple :
@deprecated Utiliser <code>setVisible(true)</code> la place

Vous pouvez ajouter des liens hypertexte vers dautres parties connexes de la documentation javadoc, ou vers dautres documents externes, laide des balises @see et @link:
@see rfrence

Cette balise ajoute un lien hypertexte dans la section "see also" (voir aussi). Elle peut tre employe
avec les classes et les mthodes. Ici, rfrence peut tre lun des choix suivants :
package.classe#fonctionnalit label
<a href="...">label</a>
"texte"

La premire possibilit est la plus utile. Vous fournissez le nom dune classe, dune mthode ou
dune variable, et javadoc insre un lien hypertexte vers la documentation. Par exemple,
@see com.horstmann.corejava.Employee#raiseSalary(double)

cre un lien vers la mthode raiseSalary(double) dans la classe com.horstmann.corejava.Employee. Vous pouvez omettre le nom du package ou la fois les noms de package et de
classe. La fonctionnalit est alors recherche dans le package ou la classe courants.
Notez que vous devez utiliser un #, et non un point, pour sparer la classe du nom de la mthode ou
de la variable. Le compilateur Java lui-mme est assez fut pour dterminer la signification du caractre point, comme sparateur entre les packages, les sous-packages, les classes, les classes internes,
et les mthodes ou variables. Lutilitaire javadoc, lui, nest pas aussi pointu, et vous devez laider.

Livre Java .book Page 171 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

171

Si la balise @see est suivie dun caractre <, vous devez spcifier un lien hypertexte. Le lien peut
concerner nimporte quelle adresse URL. Par exemple :
@see <a href="www.horstmann.com/corejava.html">The Core Java home page</a>

Dans chaque cas, vous pouvez spcifier un label facultatif, qui apparatra en tant quancre du lien.
Si vous omettez le label, le nom du code cible ou lURL apparatront lutilisateur en tant quancre.
Si la balise @see est suivie dun caractre ", le texte saffiche dans la section "see also". Par exemple :
@see "Core Java 2 volume 2"

Vous pouvez ajouter plusieurs balises @see pour une fonctionnalit, mais vous devez les regrouper
ensemble.
Vous pouvez aussi placer les liens hypertexte vers dautres classes ou mthodes, nimporte o dans
vos commentaires. Vous insrez une balise spciale sous la forme {@link package.classe#fonctionnalit label} nimporte o dans un commentaire. La description de la fonctionnalit suit les
mmes rgles que pour la balise @see.

Commentaires de package et densemble


Vous placez les commentaires de classe, de mthode et de variable directement dans les fichiers
source Java, dlimits par les commentaires de documentation /** . . . */. Toutefois, pour gnrer des commentaires de package, vous devez ajouter un fichier appel package.html dans chaque
rpertoire de package. Tout texte entre les balises <BODY>...</BODY> est extrait.
Vous pouvez aussi fournir un commentaire densemble pour tous les fichiers source. Placez-le dans
un fichier appel overview.html, situ dans le rpertoire parent qui contient tous les fichiers
source. Tout le texte entre les balises <BODY>...</BODY> est extrait. Ce commentaire de vue
densemble saffiche lorsque lutilisateur slectionne "Overview" dans la barre de navigation.

Extraction des commentaires


Ici, docDirectory est le nom du rpertoire o vous voulez stocker les fichiers HTML. Voici les
tapes suivre :
1. Positionnez-vous dans le rpertoire contenant les fichiers source documenter. Si vous devez
documenter des packages imbriqus, tels que com.horstmann.corejava, vous devez vous trouver dans le rpertoire qui contient le sous-rpertoire com (le rpertoire qui contient le fichier
overview.html, le cas chant).
2. Excutez la commande
javadoc -d docDirectory nomDuPackage

pour un package simple. Ou excutez


javadoc -d docDirectory nomDuPackage1 nomDuPackage2...

pour documenter plusieurs packages. Si vos fichiers se trouvent dans le package par dfaut,
excutez
javadoc -d docDirectory *.java

la place.

Livre Java .book Page 172 Jeudi, 25. novembre 2004 3:04 15

172

Au cur de Java 2 - Notions fondamentales

Si vous avez omis loption -d docDirectory, les fichiers HTML sont extraits du rpertoire courant.
Cela peut devenir compliqu et nest pas recommand.
Le programme javadoc peut tre personnalis par de nombreuses options de ligne de
commande. Vous pouvez, par exemple, employer les options -author et -version pour inclure
les balises @author et @version dans la documentation (elles sont omises par dfaut). Une autre
option utile est -link, pour inclure des liens hypertexte vers les classes standard. Par exemple, si
vous utilisez la commande
javadoc link http://java.sun.com/j2se/5.0/docs/api *.java

toutes les classes de la bibliothque standard sont automatiquement lies la documentation du site
Web de Sun.
Pour dcouvrir dautres options, vous pouvez consulter la documentation en ligne de lutilitaire
javadoc ladresse http://java.sun.com/j2se/javadoc/.
INFO
Si vous avez besoin de personnalisation supplmentaire, par exemple pour produire une documentation sous un
format autre que HTML, vous pouvez fournir votre propre doclet pour gnrer la sortie sous la forme que vous
voulez. Il sagit l dune requte trs particulire, et vous devez vous reporter la documentation en ligne spcifique
ladresse suivante : http://java.sun.com/j2se/javadoc

ASTUCE
DocCheck est un doclet utile disponible ladresse http://java.sun.com/j2se/javadoc/doccheck/. Il analyse un jeu de
fichiers source la recherche des commentaires de documentation manquants.

Conseils pour la conception de classes


Sans vouloir tre exhaustif ou ennuyeux, nous allons terminer ce chapitre par quelques conseils qui
permettront vos classes de faire bonne figure dans les cercles lgants de la POO.
1. Les donnes doivent tre prives.
Cest une rgle absolue : toute exception viole le principe dencapsulation. Vous pouvez crire
en cas de besoin une mthode daccs ou une mthode daltration, mais les champs proprement
dits doivent rester privs. Lexprience a montr que la reprsentation des donnes peut changer,
mais que la manire dont on les utilise change plus rarement. Lorsque les donnes sont prives,
une modification de leur reprsentation naffecte pas lutilisateur de la classe, et les bogues sont
plus facilement dtectables.
2. Initialisez toujours les donnes.
Java ninitialise pas les variables locales votre place, mais il initialise les champs
dinstance des objets. Ne vous fiez pas aveuglment aux valeurs par dfaut ; initialisez
explicitement les variables en spcifiant leur valeur par dfaut, soit dans la classe, soit dans
tous les constructeurs.

Livre Java .book Page 173 Jeudi, 25. novembre 2004 3:04 15

Chapitre 4

Objets et classes

173

3. Nabusez pas des types de base dans une classe.


Le principe consiste remplacer plusieurs champs (apparents) par une autre classe. Vos classes seront ainsi plus aises comprendre et modifier. Par exemple, dans une classe Customer,
remplacez
private
private
private
private

String
String
String
String

street;
city;
state;
zip;

par une nouvelle classe nomme Address. De cette manire, vous pourrez facilement faire face
une ventuelle modification de la prsentation des adresses (pour le courrier international, par
exemple).
4. Tous les champs nont pas besoin de mthodes daccs et de mthodes daltration.
Vous pouvez avoir besoin de connatre et de modifier le salaire dun employ. En revanche, une
fois lobjet construit, il nest pas ncessaire de modifier sa date dembauche. Bien souvent, les
objets contiennent des champs dinstance qui ne doivent pas tre lus ni modifis par dautres
par exemple, le tableau des codes postaux dans une classe Address.
5. Utilisez un format standard pour la dfinition de vos classes.
Nous prsentons toujours le contenu des classes dans lordre suivant :
lments publics ;
lments accessibles au package ;
lments privs.
Chaque section est organise ainsi :
mthodes dinstance ;
mthodes statiques ;
champs dinstance ;
champs statiques.
Aprs tout, les utilisateurs de votre classe sont davantage concerns par linterface publique que
par les dtails de limplmentation prive. Et ils sintressent plus aux mthodes quaux
donnes.
En fait, il nexiste pas de convention universelle concernant le meilleur style. Le guide Sun du
langage de programmation Java recommande de lister dabord les champs, puis les mthodes.
Quel que soit le style que vous adopterez, lessentiel est de rester cohrent.
6. Subdivisez les classes ayant trop de responsabilits.
Ce conseil peut sembler vague, car le terme "trop" est relatif. En bref, chaque fois quil existe
une possibilit manifeste de diviser une classe complexe en deux classes conceptuellement plus
simples, profitez de loccasion (mais nexagrez pas, la complexit renatra si vous crez trop de
petites sous-classes).

Livre Java .book Page 174 Jeudi, 25. novembre 2004 3:04 15

174

Au cur de Java 2 - Notions fondamentales

Voici un exemple de conception maladroite :


public class CardDeck // conception maladroite
{
public
public
public
public
public

CardDeck() { . . . }
void shuffle() { . . . }
int getTopValue() { . . . }
int getTopSuit() { . . . }
void draw() { . . . }

private int[] value;


private int[] suit;
}

Cette classe implmente en ralit deux concepts spars : un jeu de cartes avec ses mthodes
shuffle et draw, et une carte avec les mthodes permettant dinspecter la valeur et la couleur
dune carte. Il est logique ici dintroduire une classe Card reprsentant une carte individuelle.
Vous avez maintenant deux classes, avec chacune ses propres responsabilits :
public class CardDeck
{
public CardDeck() { . . . }
public void shuffle() { . . . }
public Card getTop() { . . . }
public void draw() { . . . }
private Card[] cards;
}
public class Card
{
public Card(int aValue, int aSuit) { . . . }
public int getValue() { . . . }
public int getSuit() { . . . }
private int value;
private int suit;
}

7. Donnez des noms significatifs vos classes et vos mthodes.


Les variables doivent toujours avoir un nom reprsentatif de leur contenu. Il en va de mme pour
les classes (il est vrai que la bibliothque standard contient quelques exemples contestables,
comme la classe Date qui concerne lheure).
Un bon principe consiste nommer les classes avec un substantif (Order) ou un substantif associ un adjectif (RushOrder). Pour les mthodes, respectez la convention standard en faisant
dbuter leur nom par un prfixe en minuscules : get pour les mthodes daccs (getSalary) et
set pour les mthodes daltration (setSalary).

Livre Java .book Page 175 Jeudi, 25. novembre 2004 3:04 15

5
Lhritage
Au sommaire de ce chapitre

Classes, superclasses et sous-classes


Object: la superclasse cosmique
Listes de tableaux gnriques
Enveloppes dobjets et autoboxing
Rflexion
Enumration de classes
Conseils pour lutilisation de lhritage
Le chapitre prcdent tudiait les classes et les objets. Celui-ci traite de lhritage, essentiel dans la
programmation oriente objet. Lide qui sous-tend ce concept est que vous pouvez crer de nouvelles classes bases sur des classes existantes. Lorsque vous hritez dune classe, vous rutilisez (ou
hritez de) ses mthodes et champs, et ajoutez de nouveaux champs pour adapter votre classe de
nouvelles situations. Cette technique est essentielle en programmation Java.
Si votre exprience concerne essentiellement les langages procduraux, comme C, Visual Basic ou
COBOL, nous vous conseillons de lire attentivement ce chapitre. Les programmeurs C++ chevronns, ainsi que ceux qui ont dj expriment un langage orient objet comme Smalltalk, seront ici en
territoire connu ; il existe nanmoins de nombreuses diffrences entre limplmentation de lhritage
en Java et son implmentation dans les autres langages orients objet.
La dernire partie de ce chapitre couvre la rflexion, la capacit den savoir plus au sujet des classes
et de leurs proprits dans un programme en cours dexcution. La rflexion est une fonctionnalit
puissante, mais indniablement complexe. Elle concerne plus les concepteurs doutils que les
programmeurs dapplication, et vous pouvez donc vous contenter de survoler cette section dans un
premier temps, pour y revenir plus tard.

Livre Java .book Page 176 Jeudi, 25. novembre 2004 3:04 15

176

Au cur de Java 2 - Notions fondamentales

Classes, superclasses et sous-classes


Revenons la classe Employee, dj prsente au Chapitre 4. Supposons que vous travailliez pour
une entreprise au sein de laquelle les directeurs (managers) sont traits diffremment des autres
employs. Les directeurs sont aussi des employs sous bien des aspects. Tout comme les employs,
ils reoivent un salaire. Toutefois, tandis que les employs sont censs excuter leurs tches en
change de leur salaire, les dirigeants reoivent un bonus sils atteignent leurs objectifs. Cest le
genre de situation qui appelle fortement lhritage. Pour quelle raison ? Parce quil faut dfinir une
nouvelle classe, Manager, et y ajouter des fonctionnalits. Vous pouvez cependant conserver une
partie de ce que vous avez dj programm dans la classe Employee, et tous les champs de la classe
dorigine seront prservs. Dune faon plus abstraite, disons quil existe une relation "est" entre
Manager et Employee. Tout directeur est un employ : cette relation dtat (ou dappartenance) est le
flambeau de lhritage.
Voici comment dfinir une classe Manager qui hrite de la classe Employee. Le mot cl extends est
employ en Java pour signifier lhritage.
class Manager extends Employee
{
mthodes et champs ajouts
}

INFO C++
Lhritage est comparable en Java et en C++. Java utilise le mot cl extends au lieu de :. Tout hritage en Java est
public ; il nexiste pas danalogie avec les fonctionnalits C++ dhritage priv et protg.

Le mot cl extends signifie que vous crez une nouvelle classe qui drive dune classe
existante. La classe existante est appele superclasse, classe de base ou encore classe parent
(voire classe anctre). La nouvelle classe est appele sous-classe, classe drive ou classe
enfant. Les termes superclasse et sous-classe sont les plus courants en programmation Java,
bien que certains programmeurs prfrent lanalogie parent/enfant, qui convient bien la notion
dhritage.
La classe Employee est une superclasse, mais ce nest pas parce quelle serait suprieure une sousclasse ou contiendrait plus de fonctionnalits. En fait, cest le contraire : les sous-classes offrent plus
de fonctionnalits que leur superclasse. Par exemple, comme vous le verrez lors de lexamen du reste
de la classe Manager, cette dernire encapsule plus de donnes et possde plus de fonctionnalits que
sa superclasse Employee.
INFO
En anglais, les prfixes super et sub (sous) sont issus du langage des ensembles employ en informatique thorique
et en mathmatiques. Lensemble de tous les employs contient lensemble de tous les directeurs ; on dit quil sagit
dun superensemble de lensemble des directeurs. En dautres termes, lensemble de tous les directeurs est un sousensemble de lensemble de tous les employs.

Livre Java .book Page 177 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

177

Notre classe Manager a un nouveau champ pour stocker le bonus, et une nouvelle mthode pour le
dfinir :
class Manager extends Employee
{
. . .
public void setBonus(double b)
{
bonus = b;
}
private double bonus;
}

Ces mthodes et champs nont rien de particulier. Si vous avez un objet Manager, vous pouvez
simplement appliquer la mthode setBonus:
Manager boss = . . .;
boss.setBonus(5000);

Vous ne pouvez pas appliquer la mthode setBonus un objet Employee, elle ne fait pas partie des
mthodes dfinies dans la classe Employee.
Vous pouvez cependant utiliser des mthodes telles que getName et getHireDay avec les objets
Manager. Mme si ces mthodes ne sont pas explicitement dfinies dans la classe Manager, celle-ci
en hrite automatiquement de la superclasse Employee.
De mme, les champs name, salary et hireDay sont hrits de la superclasse. Chaque objet Manager
a quatre champs : name, salary, hireDay et bonus.
Lors de la dfinition dune sous-classe par extension de sa superclasse, il vous suffit dindiquer les
diffrences entre les sous-classes et la superclasse. Lors de la conception de classes, vous placez
les mthodes les plus gnrales dans la superclasse, et celles plus spcialises dans la sous-classe.
La mise en commun de fonctionnalits dans une superclasse est trs frquente en programmation
oriente objet.
Cependant, certaines des mthodes de la superclasse ne conviennent pas pour la sous-classe Manager. En particulier, la mthode getSalary, qui doit renvoyer la somme du salaire de base et du
bonus. Vous devez fournir une nouvelle mthode pour remplacer celle de la superclasse :
class Manager extends Employee
{
. . .
public double getSalary()
{
. . .
}
. . .
}

Comment pouvez-vous implmenter cette mthode ? Au premier abord, cela parat simple :
renvoyez simplement la somme des champs salary et bonus:
public double getSalary()
{
return salary+bonus; // ne marche pas
}

Livre Java .book Page 178 Jeudi, 25. novembre 2004 3:04 15

178

Au cur de Java 2 - Notions fondamentales

Cela ne peut pas marcher. La mthode getSalary de la classe Manager na pas daccs direct aux
champs privs de la superclasse. Cela signifie que la mthode getSalary de la classe Manager ne
peut pas accder directement au champ salary, mme si chaque objet Manager a un champ
appel salary. Seules les mthodes de la classe Employee ont accs aux champs privs. Si les
mthodes de Manager veulent accder ces champs privs, elles doivent procder comme les
autres mthodes : utiliser linterface publique, cest--dire la mthode publique getSalary de la
classe Employee.
Essayons nouveau. Nous voulons donc appeler getSalary au lieu daccder simplement au champ
salary:
public double getSalary()
{
double baseSalary = getSalary(); // ne marche toujours pas
return baseSalary+bonus;
}

Le problme est que lappel getSalary ralise simplement un appel lui-mme, puisque la
classe Manager a une mthode getSalary (prcisment la mthode que nous essayons dimplmenter). Il sensuit une srie infinie dappels la mme mthode, ce qui entrane un plantage du
programme.
Nous devons prciser que nous voulons appeler la mthode getSalary de la superclasse Employee,
et non celle de la classe courante. Vous devez pour cela utiliser le mot cl super, lappel
super.getSalary()

appelle la mthode getSalary de la classe Employee. Voici la version correcte de la mthode


getSalary pour la classe Manager:
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary+bonus;
}

INFO
Certains pensent que super est analogue la rfrence this. Cette analogie nest toutefois pas exacte super
nest pas une rfrence un objet. Par exemple, vous ne pouvez pas affecter la valeur super une autre variable
objet. super est un mot cl spcial qui demande au compilateur dinvoquer la mthode de la superclasse.

Vous avez vu quune sous-classe pouvait ajouter des champs et quelle pouvait ajouter ou remplacer
des mthodes de la superclasse. Cependant, lhritage ne peut pas ter des champs ou des mthodes.
INFO C++
Java utilise le mot cl super pour appeler une mthode de superclasse. En C++, vous utilisez le nom de la superclasse
avec loprateur ::. Par exemple, la mthode getSalary de la classe Manager appellera Employee::getSalary
au lieu de super.getSalary.

Livre Java .book Page 179 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

179

Enfin, nous allons fournir un constructeur :


public Manager(String n, double s, int year, int month, int day)
{
super(n, s, year, month, day);
bonus = 0;
}

Ici, le mot cl super a une signification diffrente. Linstruction


super(n, s, year, month, day);

est un raccourci pour dire "appeler le constructeur de la superclasse Employee avec n, s, year,
month et day comme paramtres".
Puisque le constructeur de Manager ne peut pas accder aux champs privs de la classe Employee,
ils doivent tre initialiss par lintermdiaire dun constructeur. Le constructeur est invoqu
laide de la syntaxe spciale super. Lappel utilisant super doit tre la premire instruction dans
le constructeur pour la sous-classe.
Si le constructeur de la sous-classe nappelle pas explicitement un constructeur de la superclasse, le
constructeur par dfaut est appel (sans paramtre). Au cas o la superclasse ne possderait pas
de constructeur par dfaut et o aucun autre constructeur nest appel explicitement partir du
constructeur de la sous-classe le compilateur Java indique une erreur.
INFO
Souvenez-vous que le mot cl this a deux significations : dfinir une rfrence au paramtre implicite, et appeler
un autre constructeur de la mme classe. Le mot cl super a galement deux significations : invoquer une mthode
de superclasse, et invoquer un constructeur de superclasse. Lorsquils sont utiliss pour invoquer des constructeurs,
les mots cls this et super sont trs proches. Les appels de constructeur ne peuvent qutre la premire instruction
dans un autre constructeur. Les paramtres de construction sont passs, soit un autre constructeur de la mme
classe (this), soit un constructeur de la superclasse (super).

INFO C++
Dans un constructeur C++, vous nappelez pas super, mais vous utilisez la syntaxe de liste dinitialisation pour
construire la superclasse. Le constructeur Manager a laspect suivant en C++ :
Manager::Manager(String n, double s, int year, int month,
int day) // C++
{
bonus = 0;
}

La consquence de la redfinition de la mthode getSalary pour les objets Manager, est que pour
les directeurs, le bonus est automatiquement ajout au salaire.
Pour comprendre ce fonctionnement, crons un nouveau directeur et dfinissons son bonus :
Manager boss = new Manager("Carl Cracker", 80000,
1987, 12, 15);
boss.setBonus(5000);

Livre Java .book Page 180 Jeudi, 25. novembre 2004 3:04 15

180

Au cur de Java 2 - Notions fondamentales

Crons un tableau de trois employs :


Employee[] staff = new Employee[3];

Affectons ce tableau le personnel de lentreprise (employs et directeurs) :


staff[0]
staff[1]
1989,
staff[2]
1990,

= boss;
= new Employee("Harry Hacker", 50000,
10, 1);
= new Employee("Tony Tester", 40000,
3, 15);

Affichons le salaire de chacun :


for (Employee e: staff)
System.out.println(e.getName()+" "
+e.getSalary());;

Cette boucle affiche les donnes suivantes :


Carl Cracker 85000.0
Harry Hacker 50000.0
Tommy Tester 40000.0

staff[1] et staff[2] affichent leur salaire de base, car ce sont des objets de la classe Employee.
En revanche, staff[0] est un objet Manager et sa mthode getSalary ajoute le bonus au salaire de
base.
Ce qui est important, cest que lappel
e.getSalary()

slectionne la mthode getSalary correcte. Notez que le type dclar de e est Employee, mais que
le type rel de lobjet auquel e fait rfrence peut tre soit Employee (si i vaut 1 ou 2), soit Manager
(si i vaut 0).
Lorsque e fait rfrence un objet Employee, lappel e.getSalary() appelle la mthode
getSalary de la classe Employee. Si toutefois, e fait rfrence un objet Manager, cest la
mthode getSalary de la classe Manager qui est appele la place. La machine virtuelle
connat le type rel de lobjet auquel e fait rfrence et invoque par consquent la mthode qui
convient.
Cette possibilit pour une variable objet (comme la variable e) de pouvoir faire rfrence plusieurs
types est appele polymorphisme. La slection automatique de la mthode approprie lors de
lexcution est appele liaison dynamique. Nous reviendrons sur ces deux concepts plus en dtail
dans ce chapitre.
INFO C++
En Java, vous navez pas besoin de dclarer une mthode comme virtuelle. La liaison dynamique est le comportement
par dfaut. Si vous ne voulez pas quune mthode soit virtuelle, attribuez-lui le mot cl final (nous y reviendrons
dans ce chapitre).

LExemple 5.1 contient un programme qui montre la faon dont diffre le calcul du salaire pour les
objets Employee et Manager.

Livre Java .book Page 181 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

Exemple 5.1 : ManagerTest.java


import java.util.*;
public class ManagerTest
{
public static void main(String[] args)
{
// construire un objet Manager
Manager boss = new Manager("Carl Cracker", 80000,
1987, 12, 15);
boss.setBonus(5000);
Employee[] staff = new Employee[3];
// remplir le tableau staff avec des objets Manager et Employee
staff[0]
staff[1]
1989,
staff[2]
1990,

= boss;
= new Employee("Harry Hacker", 50000,
10, 1);
= new Employee("Tommy Tester", 40000,
3, 15);

// imprimer les infos concernant tous les objets Employee


for (Employee e: staff)
System.out.println("name="+e.getName()
+",salary="+e.getSalary());
}
}
class Employee
{
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}

181

Livre Java .book Page 182 Jeudi, 25. novembre 2004 3:04 15

182

Au cur de Java 2 - Notions fondamentales

public void raiseSalary(double byPercent)


{
double raise = salary * byPercent / 100;
salary += raise;
}
private String name;
private double salary;
private Date hireDay;
}
class Manager extends Employee
{
/**
@param n Nom de lemploy
@param s Le salaire
@param year Lanne dembauche
@param month Le mois dembauche
@param day Le jour dembauche
*/
public Manager(String n, double s,
int year, int month, int day)
{
super(n, s, year, month, day);
bonus = 0;
}
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary+bonus;
}
public void setBonus(double b)
{
bonus = b;
}
private double bonus;
}

Hirarchie dhritage
Lhritage nest pas oblig de se limiter une seule couche de classes drives. Nous pourrions, par
exemple, crer une classe Executive (Cadre) qui prolonge Manager. Lensemble de toutes les classes drives dune superclasse commune est appel hirarchie dhritage ou hirarchie des classes,
(voir Figure 5.1). Le chemin daccs une classe particulire vers ses anctres, dans la hirarchie
dhritage, se nomme la chane dhritage.
Il existe gnralement plusieurs chanes descendant dune mme classe anctre. Vous pouvez former
une sous-classe Programmer ou Secretary qui prolonge la classe Employee: elles nauront aucune
relation avec la classe Manager (ni entre elles). Le processus de cration de classes drives nest pas
limit.

Livre Java .book Page 183 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

183

INFO C++
Java ne prend pas en charge lhritage multiple (pour savoir comment rcuprer la majeure partie des fonctionnalits
de lhritage multiple, voir la section sur les interfaces au prochain chapitre).

Figure 5.1
Hirarchie dhritage
de Employee.

Employee

Manager

Secretary

Programmer

Executive

Polymorphisme
Il existe une rgle simple pour savoir si lhritage est ou non le concept envisager pour vos
donnes. La relation "est" dit que tout objet de la sous-classe est un objet de la superclasse. Par
exemple, chaque directeur est un employ. Il est par consquent logique que la classe Manager soit
une sous-classe de la classe Employee. La rciproque nest videmment pas vraie chaque
employ nest pas un directeur.
Une autre faon de formuler cette relation est le principe de substitution. Il prcise que vous pouvez
utiliser un objet dune sous-classe chaque fois que le programme attend un objet dune superclasse.
Vous pouvez ainsi affecter un objet dune sous-classe une variable de la superclasse :
Employee e;
e = new Employee(. . .);
// objet Employee attendu
e = new Manager(. . .); // OK, Manager peut aussi tre utilis

Dans le langage Java, les variables objet sont polymorphes. Une variable du type Employee peut
faire rfrence un objet du type Employee ou un objet de toute sous-classe de la classe Employee
(tel que Manager, Executive, Secretary, etc.).
Nous tirons parti de ce principe dans lExemple 5.1 :
Manager boss = new Manager(. . .);
Employee[] staff = new Employee[3];
staff[0] = boss;

Dans ce cas, les variables staff[0] et boss font rfrence au mme objet. Cependant, staff[0] est
considr par le compilateur comme seulement un objet Employee.
Cela signifie que vous pouvez appeler
boss.setBonus(5000); // OK

Livre Java .book Page 184 Jeudi, 25. novembre 2004 3:04 15

184

Au cur de Java 2 - Notions fondamentales

mais pas
staff[0].setBonus(5000); // ERREUR

Le type dclar de staff[0] est Employee, et la mthode setBonus nest pas une mthode de la
classe Employee.
Vous ne pouvez toutefois pas affecter une rfrence de superclasse une variable de sous-classe. Par
exemple, laffectation suivante est incorrecte :
Manager m = staff[i]; // ERREUR

La raison de cette interdiction est simple : tous les employs ne sont pas directeurs. Si cette affectation russissait et que m puisse faire rfrence un objet Employee qui ne soit pas un directeur, il
serait possible dappeler ultrieurement m.setBonus(...), et une erreur dexcution sensuivrait.
ATTENTION
En Java, il est possible de transformer des tableaux de rfrences de sous-classes en tableaux de rfrences de superclasses, sans transtypage. Envisageons par exemple un tableau de directeurs
Manager[] managers = new Manager[10];

La conversion de ce tableau en tableau Employee[] est autorise :


Employee[] staff = managers; // OK

Pourquoi pas, aprs tout ? Si manager[i] est un objet Manager, cest galement un objet Employee. En fait, un
vnement surprenant survient. Noubliez pas que les managers et staff font rfrence au mme tableau. Etudiez
maintenant linstruction
staff[0] = new Employee("Harry Hacker", ...);

Le compilateur autorisera cette instruction avec plaisir. Mais staff[0] et manager[0] constituent la mme rfrence, on dirait donc que nous avons russi faire passer clandestinement un employ dans les rangs de la direction.
Ceci serait trs dconseill : lappel managers[0].setBonus(1000) tenterait daccder une instance inexistante et corromprait la mmoire voisine.
Pour viter toute corruption, tous les tableaux se souviennent du type dlment cr, et ils surveillent que seules les
rfrences compatibles y soient stockes. Par exemple, le tableau cr sous la forme new Manager[10] se souvient
quil sagit uniquement dun tableau de directeurs. Tenter de stocker une rfrence Employee entrane une exception
de type ArrayStoreException.

Liaison dynamique
Il importe de bien comprendre ce qui se passe lorsquun appel de mthode est appliqu un objet.
Voici les dtails :
1. Le compilateur examine le type dclar de lobjet et le nom de la mthode. Supposons que nous
appelions x.f(param), et que le paramtre implicite x soit dclar comme tant un objet de la
classe C. Notez quil peut y avoir plusieurs mthodes, toutes avec le mme nom f, mais avec des
types de paramtres diffrents. Il peut, par exemple, y avoir une mthode f(int) et une mthode
f(String). Le compilateur numre toutes les mthodes appeles f dans la classe C et toutes
les mthodes public appeles f dans les superclasses de C.
Maintenant, le compilateur connat tous les candidats possibles pour la mthode appeler.

Livre Java .book Page 185 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

185

2. Le compilateur dtermine ensuite les types des paramtres qui sont fournis dans lappel de la
mthode. Si, parmi toutes les mthodes nommes f, il en existe une seule dont les types de paramtres correspondent exactement aux paramtres fournis, cette mthode est choisie pour lappel.
Ce processus est appel rsolution de surcharge. Par exemple, dans un appel x.f("Hello"), le
compilateur choisira f(String) et non f(int). La situation peut devenir complexe du fait des
conversions de type (int vers double, Manager vers Employee, etc.). Si le compilateur ne peut
pas trouver de mthode avec les types de paramtres qui correspondent, ou sil existe plusieurs
mthodes pouvant convenir aprs application des conversions, le compilateur renvoie une erreur.
Maintenant, le compilateur connat le nom et le type des paramtres de la mthode devant tre
appele.
INFO
Souvenez-vous que le nom et la liste des types de paramtres pour une mthode sont appels signature de la
mthode. Par exemple, f(int) et f(String) sont deux mthodes de mme nom, mais prsentant des signatures
diffrentes. Si vous dfinissez une mthode dans une sous-classe avec la mme signature quune mthode dune
superclasse, vous remplacez cette mthode. Le type renvoy ne fait pas partie de la signature. Toutefois, lorsque vous
remplacez une mthode, vous devez conserver un type de retour compatible. Avant le JDK 5.0, les types de retours
devaient tre identiques. Mais la sous-classe peut maintenant modifier le type de retour dune mthode remplace
en sous-type du type dorigine. Supposons par exemple que la classe Employee ait un
public Employee getBuddy() { ... }

la sous-classe Manager peut alors remplacer cette mthode par


public Manager getBuddy() { ... } // OK avec JDK 5.0

On dit que les deux mthodes getBuddy ont des types de retours covariants.

3. Si la mthode est private, static, final ou un constructeur, le compilateur sait exactement


quelle mthode appeler (le modificateur final est expliqu dans la section suivante). Cela
sappelle une liaison statique. Sinon, la mthode appeler dpend du type rel du paramtre
implicite, et la liaison dynamique doit tre utilise au moment de lexcution. Dans notre exemple,
le compilateur gnrerait une instruction pour appeler f(String) avec liaison dynamique.
4. Lorsque le programme sexcute et quil utilise la liaison dynamique pour appeler une mthode,
la machine virtuelle doit appeler la version de la mthode approprie pour le type rel de lobjet
auquel x fait rfrence. Supposons que le type rel soit D, une sous-classe de C. Si la classe D dfinit une mthode f(String), celle-ci est appele. Sinon, la superclasse de D est examine pour
rechercher une mthode f(String),etc.
Excuter cette recherche chaque fois quune mthode est appele serait une perte de temps. La
machine virtuelle prcalcule pour chaque classe une table de mthodes, qui liste toutes les signatures de mthodes et les mthodes relles appeler. Lorsquune mthode est rellement appele,
la machine virtuelle fait une simple recherche dans la table. Dans notre exemple, la machine
virtuelle consulte la table de mthodes pour la classe D et recherche la mthode appeler pour
f(String). Il peut sagir de D.f(String) ou de X.f(String), o X est une superclasse
quelconque de D.
Il existe une variante ce scnario. Si lappel est super.f(param), le compilateur consulte la
table des mthodes de la superclasse du paramtre implicite.

Livre Java .book Page 186 Jeudi, 25. novembre 2004 3:04 15

186

Au cur de Java 2 - Notions fondamentales

Nous allons examiner ce processus en dtail dans lappel e.getSalary() de lExemple 5.1. Le type
dclar de e est Employee. La classe Employee possde une seule mthode appele getSalary, et
elle na pas de paramtre. Dans ce cas, nous nallons pas nous proccuper de rsolution de
surcharge.
Puisque la mthode getSalary nest pas private, static ni final, elle est lie dynamiquement.
La machine virtuelle produit des tables de mthodes pour les classes Employee et Manager. La table
Employee montre que toutes les mthodes sont dfinies dans la classe Employee elle-mme :
Employee:
getName() -> Employee.getName()
getSalary() -> Employee.getSalary()
getHireDay() -> Employee.getHireDay()
raiseSalary(double) -> Employee.raiseSalary(double)

En ralit, ce nest pas tout ; comme vous verrez plus loin dans ce chapitre, la classe Employee a une
superclasse Object de laquelle elle hrite un certain nombre de mthodes. Les mthodes de Object
sont ignores pour linstant.
La table de mthodes Manager est lgrement diffrente. Trois mthodes font partie de lhritage,
une est redfinie et une est ajoute :
Manager:
getName() -> Employee.getName()
getSalary() -> Manager.getSalary()
getHireDay() -> Employee.getHireDay()
raiseSalary(double) -> Employee.raiseSalary(double)
setBonus(double) -> Manager.setBonus(double)

A lexcution, lappel e.getSalary() est rsolu de la faon suivante :


1. Tout dabord, la machine virtuelle consulte la table de mthodes pour le type rel de e. Il peut
sagir de la table des mthodes pour Employee, Manager ou une autre sous-classe de Employee.
2. Puis, la machine virtuelle recherche dans la classe dtermine la signature de getSalary().
Elle sait maintenant quelle mthode appeler.
3. Enfin, la machine virtuelle appelle la mthode.
La liaison dynamique possde une proprit trs importante : elle rend les programmes extensibles sans quil soit ncessaire de modifier le code existant. Supposons quune nouvelle classe
Executive soit ajoute, et quil soit possible que la variable e fasse rfrence un objet de cette
classe. Le code contenant lappel e.getSalary() na pas besoin dtre recompil. La mthode
Executive.getSalary() est appele automatiquement si e fait rfrence un objet du type
Executive.
ATTENTION
Lorsque vous remplacez une mthode, la mthode de la sous-classe doit tre au moins aussi visible que celle de la
superclasse. En particulier, si la mthode de la superclasse est public, la mthode de la sous-classe doit aussi tre
dclare comme public. Il est courant domettre accidentellement le spcificateur public pour la mthode de la
sous-classe. Le compilateur proteste alors et signale que vous essayez de fournir un privilge daccs plus faible.

Livre Java .book Page 187 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

187

Empcher lhritage : les classes et les mthodes final


Dans certaines circonstances, vous souhaiterez interdire dautres programmeurs de former une
sous-classe partir dune des classes que vous avez cres. On emploie le modificateur final pour
spcifier quune classe ne peut pas tre tendue (une telle classe est aussi appele classe final).
Supposons que nous voulions empcher la cration de sous-classes partir de la classe Executive.
Il suffit pour cela dutiliser le modificateur final dans sa dclaration, de la faon suivante :
final class Executive extends Manager
{
. . .
}

Une mthode peut galement tre dclare final. Dans ce cas, aucune sous-classe ne pourra
remplacer cette mthode (toutes les mthodes dune classe final sont automatiquement des mthodes
final). Par exemple :
class Employee
{
. . .
public final String getName()
{
return name;
}
. . .
}

INFO
Souvenez-vous que les champs peuvent galement tre qualifis de final. Un champ final ne peut pas tre modifi
une fois que lobjet a t construit. Toutefois, si une classe est dclare comme final, seules les mthodes, et non
les champs, sont automatiquement final.

Il nexiste quune bonne raison de rendre une mthode ou une classe final: vrifier que la smantique ne peut pas tre transforme en sous-classe. Par exemple, les mthodes getTime et setTime de
la classe Calendar sont final. Ceci montre que les concepteurs de la classe Calendar ont pris la
responsabilit de la conversion entre la classe Date et ltat du calendrier. Aucune sous-classe ne
doit tre autorise bouleverser cet arrangement. De mme, la classe String est une classe final.
Cela signifie que personne ne peut dfinir de sous-classe de String. En dautres termes, si vous
voyez une rfrence String, vous savez quelle fait rfrence une chane et rien dautre.
Certains programmeurs considrent que vous devez dclarer toutes les mthodes sous la forme
final, moins davoir une bonne raison pour vouloir utiliser le polymorphisme. En fait, en C++ et
C#, les mthodes nutilisent le polymorphisme que si vous le demandez prcisment. Ceci peut vous
sembler un peu extrme, mais il vaut certainement mieux penser soigneusement aux mthodes et aux
classes final lorsque vous concevez une hirarchie de classe.
Aux premiers temps de Java, certains programmeurs utilisaient le mot cl final dans lespoir
dviter les liaisons dynamiques. Lorsquune mthode nest pas remplace et quelle est courte, un
compilateur peut optimiser lappel de mthode, une procdure appele inlining. Par exemple, appliquer linlining lappel e.getName() le remplace par laccs de champ e.name. Cette amlioration
est valable : les units centrales dtestent la drivation, qui interfre avec leur stratgie de prlecture

Livre Java .book Page 188 Jeudi, 25. novembre 2004 3:04 15

188

Au cur de Java 2 - Notions fondamentales

des instructions lors du traitement de linstruction actuelle. Toutefois, si getName peut tre remplac
dans une autre classe, le compilateur ne peut pas procder linlining car il ne sait pas ce que peut
faire le code de remplacement.
Heureusement, le compilateur JIT de la machine virtuelle peut se rvler bien meilleur quun
compilateur traditionnel. Il connat exactement les classes qui tendent une classe donne et peut
vrifier si une classe remplace rellement une mthode donne. Si une mthode est courte,
frquemment appele et quelle nest pas rellement remplace, le compilateur JIT peut procder
linlining de la mthode. Que se passe-t-il si la machine virtuelle charge une autre sous-classe
qui surcharge une mthode en ligne ? Loptimiseur doit annuler linlining. Lopration est lente
mais narrive que rarement.
INFO C++
En C++, une mthode nest pas lie dynamiquement par dfaut ; de plus, il est possible de spcifier la directive
inline pour que les appels la mthode soient remplacs par le code source de la mthode. Cependant, aucun
mcanisme nempche une sous-classe de remplacer une mthode de superclasse. On peut crire des classes C++ incapables davoir de descendance, mais cela exige une ruse complexe qui se justifie trs rarement (cette astuce mystrieuse est laisse au lecteur en guise dexercice. Indice : utilisez une classe de base virtuelle).

Transtypage
Nous avons appris, au Chapitre 3, que la conversion force dun type en un autre sappelle le transtypage, et que Java utilise une syntaxe spcifique pour dsigner ce mcanisme. Par exemple,
double x = 3.405;
int nx = (int)x;

convertit la valeur de lexpression x en un entier (int), en supprimant la partie dcimale.


Il est parfois ncessaire de convertir un nombre rel en nombre entier. Il peut aussi tre ncessaire de
convertir un objet dune classe en objet dune autre classe. Pour effectuer un transtypage dune rfrence dobjet, on emploie une syntaxe comparable celle du transtypage dune expression numrique. Le nom de classe cible est mis entre parenthses et plac devant la rfrence dobjet que lon
souhaite convertir. Voici un exemple :
Manager boss = (Manager)staff[0];

Il ny a quune seule raison deffectuer un transtypage dobjet utiliser ce dernier pleine capacit
lorsque son type rel a t temporairement occult. Par exemple, dans la classe ManagerTest, le
tableau staff devait tre un tableau dobjets Employee, car certains des lments du tableau taient
des employs rguliers (de type Employee). Les objets directeurs de ce tableau doivent tre transtyps en Manager (leur type rel) si lon souhaite accder leurs variables spcifiques. Remarquez que
dans lexemple de la premire section, nous nous sommes efforcs dviter le transtypage. Nous avons
initialis la variable boss avec un objet Manager avant de la stocker dans le tableau. Il nous fallait
utiliser le type correct pour dfinir le bonus du directeur.
En Java, comme vous le savez, chaque variable objet appartient un type donn, qui dcrit le genre
dobjet auquel se rfre la variable et en dtermine les capacits. Par exemple, staff[i] fait rfrence un objet Employee (et peut donc rfrencer un objet Manager).

Livre Java .book Page 189 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

189

Le compilateur sassure que lon ne promet pas plus que lon ne peut tenir lorsque vous stockez une
valeur dans une variable. Si vous affectez une rfrence dune sous-classe une variable de la superclasse, vous promettez moins, et le compilateur vous laisse faire. En revanche, si vous affectez une
rfrence de la superclasse une variable dune sous-classe, vous promettez plus. Vous devez donc
utiliser un transtypage pour que votre intention puisse tre vrifie au moment de lexcution.
Que se passe-t-il si vous tentez de transtyper un objet dans un type driv et que vous mentiez par
consquent sur le contenu de cet objet ?
Manager boss = (Manager)staff[1]; // ERREUR

Lorsque le programme sexcute, Java remarque que le type est inappropri et gnre une exception
de type ClassCastException. Si vous ninterceptez pas cette exception, le programme se termine.
Il est par consquent souhaitable de dterminer si un transtypage russira avant de lentreprendre.
A cette fin, employez loprateur instanceof, de la faon suivante :
if (staff[1] instanceof Manager)
{
boss = (Manager) staff[1];
. . .
}

Prcisons que le compilateur ne vous laissera pas effectuer un transtypage si celui-ci est invitablement
vou lchec. A titre dexemple, le transtypage
Date c = (Date)staff[1];

provoque une erreur de compilation, car Date nest pas une sous-classe de Employee.
En rsum :
m

Un transtypage dobjet ne peut sappliquer qu des objets de la mme hirarchie de classes.

Utilisez instanceof avant de procder un transtypage dune superclasse vers une sous-classe.
INFO

Le test : x instanceof C ne gnre pas dexception si x vaut null. Il renvoie simplement la valeur false. Cela est
logique, car null ne fait pas rfrence un objet, en tout cas pas un objet du type C.

En rgle gnrale, il est prfrable de ne pas convertir le type dun objet par transtypage. Dans nos
exemples, il est rarement ncessaire de transtyper un objet Employee en objet Manager. La mthode
getSalary fonctionnera correctement avec les deux objets des deux classes. La rpartition dynamique
permet de slectionner automatiquement la mthode correcte (par polymorphisme).
Lunique raison davoir recours au transtypage est lemploi dune mthode spcifique aux directeurs,
comme setBonus. Si, pour quelque raison que ce soit, il apparat important dappeler setBonus
pour un objet de type Employee, demandez-vous si cela rvle une faille dans la conception de la
superclasse. Il peut tre indiqu de revoir la conception de la superclasse et dajouter une mthode
setBonus. Noubliez pas ceci : il suffit dune exception ClassCastException non dtourne pour
interrompre lexcution du programme. En gnral, il est prfrable de limiter autant que possible
lutilisation de transtypages et de loprateur instanceof.

Livre Java .book Page 190 Jeudi, 25. novembre 2004 3:04 15

190

Au cur de Java 2 - Notions fondamentales

INFO C++
Java emploie une syntaxe de transtypage issue du C, mais elle sutilise comme lopration dynamic_cast de C++.
Par exemple :
Manager boss = (Manager)staff[1]; // Java

correspond :
Manager* boss = dynamic_cast<Manager*>(staff[1]); // C++

avec nanmoins une diffrence importante : si le transtypage choue, il ne produit pas un objet null, mais dclenche une exception. Dans ce sens, il ressemble un transtypage de rfrences en C++. Cest dommage, car en C++ vous
pouvez effectuer la vrification de type et le transtypage en une seule opration.
Manager* boss = dynamic_cast<Manager*>(staff[1]); // C++
if (boss!= NULL) . . .

En Java, il faut employer une combinaison de loprateur instanceof et du transtypage :


if (staff[1] instanceof Manager)
{
Manager boss = (Manager)staff[1];
. . .
}

Classes abstraites
A mesure que lon remonte dans la hirarchie des classes, celles-ci deviennent plus gnrales et
souvent plus abstraites. A un certain moment, la classe anctre devient tellement gnrale quon
la considre surtout comme un moule pour des classes drives et non plus comme une vritable
classe dote dinstances. Prenons lexemple dune extension de la hirarchie de notre classe
Employee. Un employ est une personne, tout comme lest un tudiant. Etendons notre hirarchie de classe pour inclure les classes Person et Student. La Figure 5.2 montre les relations
dhritage entre ces classes.
Figure 5.2
Schma de lhritage
pour Person
et ses sous-classes.

Person

Employee

Student

Pourquoi construire une classe dote dun tel niveau dabstraction ? Certains attributs, comme le
nom, concernent tout le monde. Les tudiants et les employs ont un nom, et le fait dintroduire une
superclasse commune permet de "factoriser" la mthode getName un plus haut niveau dans la
hirarchie dhritage.

Livre Java .book Page 191 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

191

Ajoutons prsent une autre mthode, getDescription, dont le but est de renvoyer une brve
description de la personne, par exemple :
an employee with a salary of $50,000.00
a student majoring in computer science

Il est facile dimplmenter cette mthode pour les classes Employee et Student. Mais quelle information pouvez-vous fournir dans la classe Person? Cette classe ne sait rien de la personne, except
son nom. Bien entendu, vous pouvez implmenter Person.getDescription() pour renvoyer une
chane vide. Mais il existe un meilleur moyen. Si vous employez le mot cl abstract, vous navez
pas besoin dimplmenter la mthode du tout :
public abstract String getDescription();
// aucune implmentation requise

Pour plus de clart, une classe possdant une ou plusieurs mthodes abstraites doit elle-mme tre
dclare abstraite :
abstract class Person
{ . . .
public abstract String getDescription();
}

En plus des mthodes abstraites, les classes abstraites peuvent possder des donnes et des mthodes
concrtes. Par exemple, la classe Person peut stocker le nom de la personne et disposer dune
mthode qui renvoie ce nom :
Person
{
public Person(String n)
{
name = n;
}
public abstract String getDescription();
public String getName()
{
return name;
}
private String name;
}

ASTUCE
De nombreux programmeurs pensent que les classes abstraites ne doivent avoir que des mthodes abstraites. Cette
vision est errone. Il est toujours prfrable de placer autant de fonctionnalits que possible dans une superclasse,
quelle soit ou non abstraite. En particulier, placez les mthodes et les champs communs (abstraits ou non) dans la
superclasse abstraite.

Les mthodes abstraites reprsentent en quelque sorte des emplacements pour les mthodes qui
seront implmentes dans les sous-classes. Lorsque vous tendez une classe abstraite, vous
avez deux possibilits. Vous pouvez laisser certaines ou toutes les mthodes abstraites indfinies.

Livre Java .book Page 192 Jeudi, 25. novembre 2004 3:04 15

192

Au cur de Java 2 - Notions fondamentales

La sous-classe doit ensuite tre qualifie dabstraite galement. Vous pouvez aussi dfinir toutes les
mthodes, la sous-classe nest alors plus abstraite.
Nous allons par exemple, dfinir une classe Student qui tend la classe abstraite Person et implmente la mthode getDescription. Puisque aucune des mthodes de la classe Student nest
abstraite, il nest pas ncessaire de la dclarer comme classe abstraite.
Une classe peut tre dclare abstract mme si elle ne possde pas de mthodes abstraites.
Les classes abstraites ne peuvent pas tre instancies. Autrement dit, si une classe est dclare
abstract, il est impossible de crer un objet de cette classe. Par exemple, lexpression
new Person("Vince Vu")

est une erreur. Vous pouvez nanmoins crer des objets de sous-classes concrtes.
Notez cependant que vous pouvez toujours crer une variable objet dune classe abstraite, mais cette
variable doit rfrencer un objet dune sous-classe non abstraite. Par exemple :
Person p = new Student("Vince Vu", "Economics");

La variable p est ici une variable du type abstrait Person qui fait rfrence une instance de la sousclasse non abstraite Student.
INFO C++
En C++, une mthode abstraite est appele "fonction virtuelle pure" (pure virtual function) et sa dclaration est
termine par = 0, de la faon suivante :
class Person // C++
{
public:
virtual string getDescription() = 0;
. . .
};

Une classe C++ est abstraite si elle possde au moins une fonction virtuelle pure. Il nexiste pas de mot cl particulier
en C++ pour dsigner une classe abstraite.

Nous allons dfinir une sous-classe concrte Student qui tend la classe abstraite Person:
class Student extends Person
{
public Student(String n, String m)
{
super(n);
major = m;
}
public String getDescription()
{
return "a student majoring in "+major;
}
private String major;
}

Livre Java .book Page 193 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

193

La classe Student dfinit la mthode getDescription. Toutes les mthodes de la classe Student
sont donc concrtes, et la classe nest plus dsormais abstraite.
Le programme de lExemple 5.2 dfinit la superclasse abstraite Person et deux sous-classes concrtes Employee et Student. Un tableau de rfrences Person est rempli avec les objets employ et
tudiant :
Person[] people = new Person[2];
people[0] = new Employee(. . .);
people[1] = new Student(. . .);

Nous affichons ensuite les noms et les descriptions de ces objets :


for (Person p = people)
System.out.println(p.getName()+", "+p.getDescription());

Certains sont dconcerts par lappel :


p.getDescription()

Nest-ce pas l un appel de mthode indfinie ? Noubliez pas que la variable p ne fait jamais rfrence un objet Person puisquil est impossible de construire un objet de la classe abstraite Person.
La variable p fait toujours rfrence un objet dune sous-classe concrte comme Employee ou
Student. Pour ces objets, la mthode getDescription est dfinie.
Auriez-vous pu omettre totalement la mthode abstraite pour la superclasse Person et simplement
dfinir les mthodes getDescription dans les sous-classes Employee et Student? Si vous laviez
fait, vous nauriez alors pas pu invoquer la mthode getDescription sur la variable p. Le compilateur
sassure que vous ninvoquez que les mthodes qui sont dclares dans la classe.
Les mthodes abstraites sont un concept important dans le langage de programmation Java. Vous les
rencontrerez le plus souvent au sein des interfaces. Pour plus dinformations concernant les interfaces,
reportez-vous au Chapitre 6.
Exemple 5.2 : PersonTest.java
import java.text.*;
import java.util.*;
public class PersonTest
{
public static void main(String[] args)
{
Person[] people = new Person[2];
// remplir le tableau people avec des objets Student et Employee
people[0]
= new Employee("Harry Hacker", 50000, 1989, 10, 1);
people[1]
= new Student("Maria Morris", "computer science");
// afficher les noms et descriptions de tous les objets Person
for (Person p: people)
System.out.println(p.getName()+", "
+p.getDescription());
}
}

Livre Java .book Page 194 Jeudi, 25. novembre 2004 3:04 15

194

Au cur de Java 2 - Notions fondamentales

abstract class Person


{
public Person(String n)
{
name = n;
}
public abstract String getDescription();
public String getName()
{
return name;
}
private String name;
}
class Employee extends Person
{
public Employee(String n, double s,
int year, int month, int day)
{
super(n);
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public String getDescription()
{
return String.format("an employee with a salary of $%.2f, salary);
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
private double salary;
private Date hireDay;
}
class Student extends Person
{

Livre Java .book Page 195 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

195

/**
@param n Nom de ltudiant
@param m La spcialit de ltudiant
*/
public Student(String n, String m)
{
// passer n au constructeur de la superclasse
super(n);
major = m;
}
public String getDescription()
{
return "a student majoring in "+major;
}
private String major;
}

Accs protg
Comme vous le savez, les champs dune classe sont gnralement dclars private et les mthodes
public. Tout lment private est invisible pour les autres classes. Nous avons expliqu au dbut de
ce chapitre que cette ccit slective sapplique galement aux sous-classes : une sous-classe na pas
accs aux champs privs de sa superclasse.
Il existe nanmoins des circonstances dans lesquelles vous voudrez limiter une mthode aux sousclasses seulement ou, plus gnralement, permettre aux mthodes dune sous-classe davoir accs
un champ de superclasse. Dans ce cas, il faut dclarer cet lment protected (protg). Par exemple, si la superclasse Employee dclare le champ hireDay comme protected plutt que private,
les mthodes de la classe Manager pourront y accder directement.
Cependant, les mthodes de la classe Manager ne pourront quaccder au champ hireDay des objets
Manager, et non des autres objets Employee. Cette restriction vite la violation du mcanisme de
protection et le risque que des sous-classes soient simplement cres pour obtenir laccs aux
champs protgs.
Dans la pratique, le modificateur protected doit tre utilis avec une grande prudence. Supposons
que votre classe soit utilise par dautres programmeurs et que vous layez dote de champs protgs. A votre insu, dautres programmeurs peuvent faire hriter de votre classe des sous-classes qui
accderont vos champs protgs. Ils risquent donc dtre contraris si vous modifiez ensuite
limplmentation de votre classe. Tout cela va lencontre de lesprit de la programmation oriente
objet, qui recommande lencapsulation des donnes.
En revanche, les mthodes protges sont plus courantes. Une classe peut dclarer une mthode
protected si celle-ci est dun usage dlicat. Cela autorise les sous-classes (qui, a priori, connaissent
bien leur anctre) utiliser cette mthode dlicate, mais les autres classes sans lien de parent nen
ont pas le droit.
Un bon exemple de ce genre de mthode est la mthode clone de la classe Object voir le Chapitre 6
pour plus de dtails.

Livre Java .book Page 196 Jeudi, 25. novembre 2004 3:04 15

196

Au cur de Java 2 - Notions fondamentales

INFO C++
En fait, les lments protected de Java sont visibles par toutes les sous-classes, mais aussi par toutes les autres classes qui se trouvent dans le mme package. La signification de protected est lgrement diffrente en C++, et la
notion de protection en Java est encore moins sre quen C++.

Rsumons les caractristiques des quatre modificateurs de visibilit de Java :


1. Private (priv). Visible uniquement par la classe.
2. Public. Visible par toutes les classes.
3. Protected (protg). Visible par le package et toutes les sous-classes.
4. Par dfaut (hlas !) aucun modificateur nest spcifi. Visible par tout le package.

Object : la superclasse cosmique


La classe Object reprsente lanctre ultime toutes les classes Java hritent de Object. Nanmoins,
vous navez jamais crire :
class Employee extends Object

La superclasse Object est prise en compte par dfaut si aucune superclasse nest explicitement
spcifie. Comme chaque classe Java drive de Object, il importe de se familiariser avec les fonctionnalits de cette classe anctre. Nous en examinerons ici les caractristiques de base ; les autres
aspects sont traits aux chapitres suivants et dans la documentation en ligne (plusieurs mthodes de
Object sont ddies aux threads voir le Chapitre 1 du Volume 2 pour plus dinformations sur les
threads).
Une variable de type Object peut rfrencer un objet de nimporte quel type :
Object obj = new Employee("Harry Hacker", 35000);

Bien entendu, une variable de type Object nest utile quen tant que conteneur gnrique pour des
valeurs arbitraires. Pour pouvoir rellement en utiliser la valeur courante, il faut connatre le type
original (effectif) et appliquer un transtypage :
Employee e = (Employee)obj;

En Java, seuls les types primitifs (nombres, caractres et valeurs boolennes) ne sont pas des objets.
Tous les types de tableaux, quils soient dobjets ou de types primitifs, sont des types de classes qui
tendent la classe Object:
Employee[] staff = new Employee[10];
obj = staff; // OK
obj = new int[10]; // OK

INFO C++
Il nexiste pas de classe racine cosmique en C++. Toutefois, en C++, tout pointeur peut tre converti en pointeur
void*.

Livre Java .book Page 197 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

197

La mthode equals
La mthode equals de la classe Object dtermine si deux objets sont gaux ou non. Telle quelle est
implmente dans la classe Object, cette mthode vrifie que les deux rfrences dobjet sont identiques. Cest un dfaut assez raisonnable : si deux objets sont identiques, ils doivent certainement
tre gaux. Cela suffit pour certaines classes. Il est peu logique, par exemple, de comparer deux
objets PrintStream des fins dgalit. Vous voudrez pourtant souvent implmenter le test dgalit
dans lequel deux objets sont considrs gaux lorsquils ont le mme tat.
Envisageons par exemple deux employs gaux sils ont le mme nom, le mme salaire et la mme date
dembauche (dans une vraie base de donnes demploys, il serait plus logique de comparer les identifiants. Nous prenons cet exemple pour montrer le mcanisme de la mise en place de la mthode equals) :
class Employee
{ // . . .
public boolean equals(Object otherObject)
{
// tester rapidement si les objets sont identiques
if (this == otherObject) return true;
// doit renvoyer false si le paramtre explicite vaut null
if (otherObject == null) return false;
/* si les classes ne correspondent pas,
elles ne peuvent pas tre gales */
if (getClass()!= otherObject.getClass())
return false;
/* nous savons maintenant que otherObject
est un objet Employee non null */
Employee other = (Employee)otherObject;
// tester si les champs ont des valeurs identiques
return name.equals(other.name)
&& salary == other.salary
&& hireDay.equals(other.hireDay);
}
}

La mthode getClass renvoie la classe dun objet nous verrons cette mthode en dtail plus loin
dans ce chapitre. Dans notre test, deux objets ne peuvent tre gaux que sils sont de la mme classe.
Lorsque vous dfinissez la mthode equals pour une sous-classe, appelez dabord equals sur la
superclasse. Si ce test ne russit pas, les objets ne peuvent pas tre gaux. Si les champs de superclasse sont gaux, vous tes prt comparer les champs dinstance de la sous-classe :
class Manager extends Employee{
. . .
public boolean equals(Object otherObject)
{
if (!super.equals(otherObject)) return false;
// super.equals vrifie que this et otherObject appartiennent
// la mme classe
Manager other = (Manager) otherObject;
return bonus == other.bonus;
}
}

Livre Java .book Page 198 Jeudi, 25. novembre 2004 3:04 15

198

Au cur de Java 2 - Notions fondamentales

Test dgalit et hritage


Comment devrait se comporter la mthode equals si les paramtres implicites et explicites nappartiennent pas la mme classe ? Ce domaine a fait lobjet dune certaine controverse. Dans lexemple
prcdent, la mthode equals renvoie false si les classes ne correspondent pas exactement. Mais
de nombreux programmeurs utilisent plutt un test instanceof:
if (!(otherObject instanceof Employee)) return false;

Cela autorise lventualit que otherObject puisse appartenir une sous-classe. Toutefois, cette
approche peut vous poser problme. Voici pourquoi. La spcification du langage Java requiert que la
mthode equals ait les proprits suivantes :
1. Quelle soit rflective : pour toute rfrence x non nulle, x.equals(x) doit renvoyer true.
2. Quelle soit symtrique : pour toutes rfrences x et y, x.equals(y) doit renvoyer true, si et
seulement si y.equals(x) renvoie true.
3. Quelle soit transitive : pour toutes rfrences x, y et z, si x.equals(y) renvoie true et
y.equals(z) renvoie true, alors x.equals(z) doit renvoyer true.
4. Quelle soit cohrente : si les objets auxquels x et y font rfrence nont pas chang, des appels
successifs x.equals(y) renvoient la mme valeur.
5. Pour toute rfrence x non nulle, x.equals(null) doit renvoyer false.
Ces rgles sont certainement raisonnables. Vous ne voudriez pas quun implmenteur de bibliothque pondre sil faut appeler x.equals(y) ou y.equals(x) lorsque vous localisez un lment dans
une structure de donnes.
Toutefois, la rgle de symtrie implique des consquences subtiles lorsque les paramtres appartiennent
diffrentes classes. Envisageons un appel
e.equals(m)

o e est un objet Employee et m, un objet Manager, tous les deux se trouvant avoir le mme nom,
salary et hireDate. Si Employee.equals utilise un test instanceof, cet appel renvoie true. Mais
cela signifie que lappel inverse
m.equals(e)

doit aussi renvoyer true la rgle de symtrie ne permet pas de renvoyer false ni de lancer une
exception.
La classe Manager est ennuye. Sa mthode equals doit tre prte se comparer tout Employee,
sans prendre en compte les informations spcifiques aux directeurs ! Tout coup, le test instanceof
apparat moins attirant !
Certains auteurs ont prtendu que le test getClass tait inadapt car il viole le principe de substitution. On cite souvent cet gard la mthode equals de la classe AbstractSet, qui teste si deux
ensembles possdent les mmes lments, dans le mme ordre. La classe AbstractSet possde
deux sous-classes concrtes, TreeSet et HashSet, qui utilisent diffrents algorithmes pour localiser
des lments de lensemble. Il est important de pouvoir comparer deux ensembles, quelle que soit
leur implmentation.

Livre Java .book Page 199 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

199

Toutefois, lexemple des ensembles est plutt spcialis. Il serait logique de dclarer AbstractSet.equals sous la forme final, car personne ne doit redfinir la smantique de lgalit du jeu (la
mthode nest pas rellement final. Ceci permet une sous-classe dimplmenter un algorithme
plus efficace pour le test dgalit).
Comme nous le voyons, il existe deux scnarios distincts :
m

Si les sous-classes peuvent avoir leur propre notion de lgalit, lexigence de symtrie vous
oblige utiliser le test getClass.

Si la notion dgalit est fixe dans la superclasse, vous pouvez utiliser le test instanceof et
permettre aux objets de diffrentes sous-classes dtre gaux entre eux.

Dans lexemple des employs et des directeurs, nous considrons que deux objets sont gaux
lorsquils ont des champs concordants. Si nous avons deux objets Manager avec le mme nom,
salaire et date dembauche, mais avec des bonus diffrents, ils doivent donc tre diffrents. Nous
avons par consquent utilis le test getClass.
Mais supposons que nous ayons utilis un ID demploy pour le test dgalit. Cette notion de
lgalit est logique pour toutes les sous-classes. Nous pourrions alors utiliser le test instanceof, et
nous dclarerions Employee.equals sous la forme final.
Notre recette pour lcriture dune mthode equals parfaite :
1. Nommez le paramtre explicite otherObject vous devrez ultrieurement le transtyper en une
autre variable que vous appellerez other.
2. Vrifiez si this se trouve tre identique otherObject:
if (this == otherObject) return true;

Cette instruction est une simple optimisation. Dans la pratique, cest trs courant. Il est plus
conomique de vrifier lidentit que de comparer les champs.
3. Testez si otherObject vaut null et renvoyez false dans ce cas. Ce test est obligatoire.
if (otherObject == null) return false;

Comparez les classes de this et otherObject. Si la smantique de equals risque de changer


dans les sous-classes, utilisez le test getClass:
if (getClass()!= otherObject.getClass()) return false;

Si la mme smantique vaut pour toutes les sous-classes, vous pouvez utiliser un test instanceof:
if (!(otherObject instanceof NomClasse)) return false;

4. Convertissez otherObject en une variable du type de votre classe :


nomDeClasse other = (nomDeClasse)otherObject

5. Comparez maintenant les champs, comme lexige votre notion dgalit. Utilisez == pour les
champs de type primitif, equals pour les champs dobjet. Renvoyez true si tous les champs
correspondent, false sinon :
return champ1 == other.champ1
&& champ2.equals(other.champ2)
&& . . .;

Livre Java .book Page 200 Jeudi, 25. novembre 2004 3:04 15

200

Au cur de Java 2 - Notions fondamentales

Si vous redfinissez equals dans une sous-classe, incluez un appel super.equals(other).


INFO
La bibliothque standard de Java contient plus de 150 implmentations des mthodes equals, avec une
disparit dutilisations de instanceof, dappels de getClass, de rcuprations des exceptions
ClassCastException ou ne faisant rien du tout. Ds lors, il ne semble pas que tous les programmeurs
comprennent bien les subtilits de la mthode equals. Rectangle, par exemple, est une sous-classe de
Rectangle2D. Ces deux classes dfinissent une mthode equals avec un test instanceof. Si lon compare
un Rectangle2D avec un Rectangle ayant les mmes coordonnes, cela renvoie true; changer les paramtres
renvoie false.

ATTENTION
Il arrive souvent de se mprendre sur le moment o implmenter la mthode equals. Retrouverez-vous le
problme ?
public class Employee
{
public boolean equals(Employee other)
{
return name.equals(other.name)
&& salary == other.salary
&& hireDay.equals(other.hireDay);
}
...
}

Cette mthode dclare le type de paramtre explicite sous la forme Employee. En consquence, il ne remplace pas
la mthode equals de la classe Object mais dfinit une mthode nayant aucun rapport.
Depuis le JDK 5.0, vous pouvez vous protger de ce type derreur en balisant les mthodes qui remplacent les mthodes
de la superclasse avec @Override:
@Override public boolean equals(Object other)

Si vous avez fait une erreur et que vous dfinissiez une nouvelle mthode, le compilateur signale lerreur. Supposons
par exemple que vous ajoutiez la dclaration suivante la classe Employee.
@Override public boolean equals(Employee other)

Lerreur signale car cette mthode ne remplace aucune mthode de la superclasse Object.
La balise @Override est une balise de mtadonnes. Le mcanisme des mtadonnes est trs gnral et extensible,
il permet aux compilateurs et aux outils de raliser des actions arbitraires. Lavenir nous dira si les concepteurs
doutils en profiteront. Dans le JDK 5.0, les implmenteurs du compilateur ont dcid de se frayer un chemin laide
de la balise @Override.

La mthode hashCode
Un code de hachage est un entier driv dun objet. Les codes de hachage doivent tre brouills : si
x et y sont deux objets distincts, la probabilit devrait tre forte que x.hashCode() et y.hashCode()

Livre Java .book Page 201 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

201

soient diffrents. Le Tableau 5.1 reprend quelques exemples de codes de hachage tirs de la mthode
hashCode de la classe String.
Tableau 5.1 : Codes de hachage tirs de la fonction hashCode

Chane

Code de hachage

Hello

140207504

Harry

140013338

Hacker

884756206

La classe String utilise lalgorithme suivant pour calculer le code de hachage :


int hash = 0;
for (int i = 0; i < length(); i++)
hash = 31 * hash + charAt(i);

La mthode hashCode est dfinie dans la classe Object. Chaque objet possde donc un code de
hachage par dfaut, extrait de ladresse mmoire de lobjet. Etudiez lexemple suivant :
String s = "Ok";
StringBuffer sb = new StringBuffer(s);
System.out.println(s.hashCode() + " " + sb.hashCode());
String t = new String("Ok");
StringBuffer tb = new StringBuffer(t);
System.out.println(t.hashCode() + " " + tb.hashCode());

Le Tableau 5.2 en prsente le rsultat.


Tableau 5.2 : Codes de hachage des chanes et tampons des chanes

Objet

Code de hachage

3030

sb

20526976

3030

tb

20527144

Sachez que les chanes s et t prsentent le mme code de hachage car, pour les chanes, ces codes
sont drivs de leur contenu. Les tampons de chanes sb et tb disposent de codes de hachage diffrents car aucune mthode hashCode na t dfinie pour la classe StringBuffer et la mthode
hashCode par dfaut de la classe Object drive le code de hachage de ladresse mmoire de lobjet.
Si vous redfinissez la mthode equals, vous devrez aussi redfinir la mthode hashCode pour les
objets que les utilisateurs pourraient insrer dans une table de hachage (nous avons trait des tables
de hachage au Chapitre 2 du Volume 2).

Livre Java .book Page 202 Jeudi, 25. novembre 2004 3:04 15

202

Au cur de Java 2 - Notions fondamentales

La mthode hashCode devrait renvoyer un entier (qui peut tre ngatif). Associez simplement les
codes de hachage des champs dinstance, de sorte que les codes des diffrents objets soient plus
largement rpartis.
Il existe, par exemple, une mthode hashCode pour la classe Employee:
class Employee
{
public int hashCode()
{
return 7 * name.hashCode()
+ 11 * new Double(salary).hashCode()
+ 13 * hireDay.hashCode();
}
. . .
}

Vos dfinitions de equals et hashCode doivent tre compatibles : si x.equals(y) est vrai, alors
x.hashCode() doit avoir la mme valeur que y.hashCode(). Si, par exemple, vous dfinissez
Employee.equals, de manire comparer les ID des employs, la mthode hashCode devra hacher
les ID, et non les noms des employs ou les adresses mmoire.
java.lang.Object 1.0

int hashCode()

Renvoie un code de hachage pour cet objet. Il peut sagir dun entier, positif ou ngatif. Les
objets gaux doivent renvoyer des codes de hachage identiques.

La mthode toString
Une autre mthode importante de la classe Object est toString, qui renvoie une chane reprsentant la valeur de lobjet. Voici un exemple typique. La mthode toString de la classe Point renvoie
une chane comme celle-ci :
java.awt.Point[x=10,y=20]

La plupart (mais pas toutes) des mthodes toString ont ce format : le nom de la classe, suivi des
valeurs de champs incluses entre crochets. Voici une implmentation de la mthode toString pour
la classe Employee:
public String toString()
{
return "Employee[name="+name
+",salary="+salary
+",hireDay="+hireDay
+"]";
}

En ralit, vous pouvez faire mieux. Au lieu de coder en dur le nom de la classe dans la mthode
toString, appelez getClass().getName() pour obtenir une chane avec le nom de la classe :
public String toString()
{
return getClass().getName()
+"[name="+name
+",salary="+salary

Livre Java .book Page 203 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

203

+",hireDay="+hireDay
+"]";
}

La mthode toString sapplique alors galement aux sous-classes.


Bien entendu, le programmeur de la sous-classe doit dfinir sa propre mthode toString et ajouter
les champs de la sous-classe. Si la superclasse utilise getClass().getName(), la sous-classe peut
simplement appeler super.toString(). Voici, par exemple, une mthode toString pour la classe
Manager:
class Manager extends Employee
{
. . .
public String toString()
{
return super.toString()
+"[bonus="+bonus
+"]";
}
}

Maintenant, un objet Manager saffiche de la faon suivante :


Manager[name=...,salary=...,hireDay=...][bonus=...]

La mthode toString est omniprsente pour une raison essentielle : chaque fois quun objet est
concatn avec une chane laide de loprateur "+", le compilateur Java invoque automatiquement
la mthode toString pour obtenir une reprsentation de lobjet sous forme de chane. Voici un
exemple :
Point p = new Point(10, 20);
String message = "La position actuelle est "+p;
// invoque automatiquement p.toString()

ASTUCE
Au lieu dcrire x.toString(), vous pouvez crire ""+x. Cette instruction concatne la chane vide avec la reprsentation textuelle de x, qui correspond exactement la reprsentation obtenue par x.toString(). A la diffrence de toString, cette instruction fonctionne, mme si x est de type primitif.

Si x est un objet quelconque et que vous appeliez


System.out.println(x);

la mthode println appelle simplement x.toString() et affiche la chane rsultante.


La classe Object dfinit la mthode toString pour afficher le nom de classe et le code de hachage
de lobjet. Par exemple, lappel
System.out.println(System.out)

produit une sortie ressemblant ceci :


java.io.PrintStream@2f6684

La raison en est que limplmenteur de la classe PrintStream na pas pris la peine de remplacer la
mthode toString.

Livre Java .book Page 204 Jeudi, 25. novembre 2004 3:04 15

204

Au cur de Java 2 - Notions fondamentales

La mthode toString est un outil prcieux de consignation. De nombreuses classes de la bibliothque de classes standard dfinissent la mthode toString comme fournisseur dinformations utiles
sur ltat dun objet. Ceci est particulirement utile pour consigner des messages, par exemple :
System.out.println("Current position = "+position);

Comme nous lexpliquons au Chapitre 11, une solution encore meilleure consisterait indiquer :
Logger.global.info("Current position = " + position);

ASTUCE
Il est fortement recommand dajouter une mthode toString chacune des classes que vous crivez. Vous et tous
les programmeurs utilisant vos classes apprcierez le support de consignation.

Le programme de lExemple 5.3 implmente les mthodes equals, hashCode et toString pour les
classes Employee et Manager.
Exemple 5.3 : EqualsTest.java
import java.util.*;
public class EqualsTest
{
public static void main(String[] args)
{
Employee alice1 = new Employee("Alice Adams", 75000,
1987, 12, 15);
Employee alice2 = alice1;
Employee alice3 = new Employee("Alice Adams", 75000,
1987, 12, 15);
Employee bob = new Employee("Bob Brandson", 50000,
1989, 10, 1);
System.out.println("alice1 == alice2: "
+(alice1 == alice2));
System.out.println("alice1 == alice3: "
+(alice1 == alice3));
System.out.println("alice1.equals(alice3): "
+alice1.equals(alice3));
System.out.println("alice1.equals(bob): "
+alice1.equals(bob));
System.out.println("bob.toString(): "+bob);
Manager carl = new Manager("Carl Cracker", 80000,
1987, 12, 15);
Manager boss = new Manager("Carl Cracker", 80000,
1987, 12, 15);
boss.setBonus(5000);
System.out.println("boss.toString(): "+boss);
System.out.println("carl.equals(boss): "
+carl.equals(boss));

Livre Java .book Page 205 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

System.out.println("alice1.hashCode(): " + alice1.hashCode());


System.out.println("alice3.hashCode(): " + alice3.hashCode());
System.out.println("bob.hashCode(): " + bob.hashCode());
System.out.println("carl.hashCode(): " + carl.hashCode());
}
}
class Employee
{
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public boolean equals(Object otherObject)
{
// test pour vrifier si les objets sont identiques
if (this == otherObject) return true;
// doit renvoyer false si le paramtre explicite vaut null
if (otherObject == null) return false;
/* si les classes ne correspondent pas,
elles ne peuvent pas tre gales */
if (getClass()!= otherObject.getClass())
return false;
/* nous savons maintenant que otherObject est
un objet Employee non null */
Employee other = (Employee)otherObject;

205

Livre Java .book Page 206 Jeudi, 25. novembre 2004 3:04 15

206

Au cur de Java 2 - Notions fondamentales

// tester si les valeurs de champs sont identiques


return name.equals(other.name)
&& salary == other.salary
&& hireDay.equals(other.hireDay);
}
public int hashCode()
{
return 7 * name.hashCode()
+ 11 * new Double(salary).hashCode()
+ 13 * hireDay.hashCode();
}
public String toString()
{
return getClass().getName()
+"[name="+name
+",salary="+salary
+",hireDay="+hireDay
+"]";
}
private String name;
private double salary;
private Date hireDay;
}
class Manager extends Employee
{
public Manager(String n, double s,
int year, int month, int day)
{
super(n, s, year, month, day);
bonus = 0;
}
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary+bonus;
}
public void setBonus(double b)
{
bonus = b;
}
public boolean equals(Object otherObject)
{
if (!super.equals(otherObject)) return false;
Manager other = (Manager)otherObject;
/* super.equals a vrifi que this et other
appartenaient la mme classe */
return bonus == other.bonus;
}

Livre Java .book Page 207 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

207

public int hashCode()


{
return super.hashCode()
+ 17 * new Double(bonus).hashCode();
}
public String toString()
{
return super.toString()
+"[bonus="+bonus
+"]";
}
private double bonus;
}
java.lang.Object 1.0

Class getClass()

Renvoie un objet class contenant des informations sur lobjet. Comme vous le verrez dans ce
chapitre, la reprsentation des classes lexcution est encapsule dans la classe Class.

boolean equals(Object otherObject)

Compare deux objets ; renvoie true si les objets pointent vers la mme zone mmoire, et false
dans le cas contraire. Vous devez remplacer cette mthode dans vos propres classes.

String toString()

Renvoie une chane dcrivant la valeur de lobjet. Vous devez remplacer cette mthode dans vos
propres classes.

Object clone()

Cre un clone de lobjet. Le systme dexcution de Java alloue de la mmoire pour la nouvelle
instance et y recopie la mmoire alloue lobjet courant.
INFO
Le clonage constitue une opration importante, mais il sagit aussi dun processus assez dlicat qui peut rserver de
mauvaises surprises aux imprudents. Nous y reviendrons lors de lexamen de la mthode clone, au Chapitre 6.

java.lang.Class 1.0

String getName()

Renvoie le nom de cette classe.

Class getSuperclass()

Renvoie la superclasse de cette classe en tant quobjet Class.

Listes de tableaux gnriques


Dans de nombreux langages de programmation en particulier C , la taille des tableaux doit tre
fixe au moment de la compilation. Les programmeurs dtestent cette contrainte : elle les oblige de
pnibles acrobaties de conception. Combien demploys y aura-t-il dans un service ? Probablement

Livre Java .book Page 208 Jeudi, 25. novembre 2004 3:04 15

208

Au cur de Java 2 - Notions fondamentales

pas plus de 100. Et si un service important a 150 employs ? Allons-nous gcher 90 entres pour
chaque service nayant que 10 employs ?
La situation est bien plus simple en Java, car il est possible de spcifier la taille dun tableau au
moment de lexcution :
int actualSize = . . .;
Employee[] staff = new Employee[actualSize];

Bien entendu, ce genre de code ne rsout pas compltement le problme de la modification dynamique des tableaux. Une fois que la taille dun tableau est spcifie, il nest pas facile de la changer. La
manire la plus simple de grer cette situation courante consiste utiliser une autre classe Java, classe ArrayList. La classe ArrayList est semblable un tableau, mais elle ajuste automatiquement
sa capacit mesure que vous ajoutez et supprimez des lments, sans que vous nayez rien faire.
Depuis le JDK 5.0, ArrayList est une classe gnrique avec un paramtre de type. Pour spcifier le
type des objets dlments inclus dans la liste de tableau, vous annexez un nom de classe entre les
signes suprieur et infrieur , sous la forme ArrayList<Employee>. Vous verrez au Chapitre 13
comment dfinir votre propre classe gnrique, mais il est inutile de connatre les caractristiques
techniques du type ArrayList pour lutiliser.
Nous dclarons et construisons ici une liste de tableau qui contient des objets Employee:
ArrayList<Employee> staff = new ArrayList<Employee>();

INFO
Les classes gnriques nexistaient pas avant le JDK 5.0. Il ny avait quune seule classe ArrayList, une collection
"fourre-tout" dont les lments taient de type Object. Si vous devez utiliser une ancienne version de Java, abandonnez simplement tous les suffixes de type <...>. Vous pouvez continuer utiliser ArrayList sans suffixe <...>
dans JDK 5.0 et versions ultrieures. On le considre comme un type "brut", dont le paramtre de type est effac.

INFO
Dans les versions encore antrieures du langage Java, les programmeurs utilisaient la classe Vector pour les
tableaux dynamiques. La classe ArrayList est toutefois plus efficace, et il nexiste plus de raison valable dutiliser
la classe Vector.

Employez la mthode add pour ajouter de nouveaux lments une liste de tableaux. Voici par
exemple comment remplir une liste de tableaux avec des objets Employee:
staff.add(new Employee("Harry Hacker", . . .));
staff.add(new Employee("Tony Tester", ...));

La classe ArrayList gre un tableau interne de rfrences Object. Ce tableau finira par tre satur.
Cest l que les listes de tableaux sont magiques : si vous appelez add et que le tableau interne soit
plein, la liste de tableaux cre automatiquement un tableau plus grand et recopie tous les objets du
plus petit tableau vers le plus grand.
Si vous connaissez dj le nombre dlments que contiendra le tableau, ou que vous puissiez
lvaluer assez prcisment, appelez la mthode ensureCapacity avant de remplir la liste de
tableaux :
staff.ensureCapacity(100);

Livre Java .book Page 209 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

209

Cet appel alloue un tableau interne de 100 objets. Les 100 premiers appels add nimpliquent aucun
repositionnement coteux.
Vous pouvez aussi passer une capacit initiale au constructeur de ArrayList:
ArrayList<Employee> staff = new ArrayList<Employee>(100);

ATTENTION
Lallocation dune liste de tableaux de la faon suivante :
new ArrayList<Employee>(100) // la capacit est de 100

nest pas la mme chose que lallocation dun nouveau tableau :


new Employee[100] // la taille est de 100

Il faut tablir une distinction entre la capacit dune liste de tableaux et la taille dun tableau. Si vous
allouez un tableau de 100 lments, le tableau disposera de 100 emplacements, prts lemploi. Une
liste de tableaux dont la capacit est de 100 lments peut potentiellement contenir 100 lments (et, en
ralit, plus de 100, au prix dallocations supplmentaires). Mais au dpart, juste aprs sa construction,
une liste de tableaux ne contient en fait aucun lment.
La mthode size renvoie le nombre rel dlments dans la liste de tableaux. Par exemple,
staff.size()

renvoie le nombre actuel dlments dans la liste de tableaux staff. Ce qui est lquivalent de
a.length

pour un tableau a.
Une fois que vous tes quasiment certain que la liste de tableaux a atteint sa taille dfinitive, vous
pouvez appeler la mthode trimToSize. Elle ajuste la taille du bloc de mmoire pour que la quantit
exacte ncessaire pour le nombre actuel dlments soit rserve. Le "ramasse-miettes" rcuprera
toute mmoire en excs.
Une fois que vous avez ajust la taille dune liste de tableaux, lajout de nouveaux lments va
dplacer de nouveau le bloc, ce qui prend du temps. Nutilisez trimToSize que si vous tes certain
de ne plus ajouter dlments la liste de tableaux.
INFO C++
La classe ArrayList est identique au modle de vecteur C++. ArrayList et vector sont tous deux des types gnriques. Mais le modle vector de C++ surcharge loprateur [] pour faciliter laccs aux lments. Comme Java
nautorise pas la surcharge des oprateurs, une opration quivalente exige un appel de mthode explicite. De plus,
les vecteurs de C++ sont copis par valeur. Si a et b sont deux vecteurs, laffectation a = b; fait de a un nouveau
vecteur, de la mme longueur que b, et tous les lments de b sont copis vers a. En Java, la mme affectation
amnera a et b rfrencer la mme liste de tableaux.

java.util.ArrayList<T> 1.2

ArrayList<T>()

Construit une liste de tableaux vide.

Livre Java .book Page 210 Jeudi, 25. novembre 2004 3:04 15

210

Au cur de Java 2 - Notions fondamentales

ArrayList<T>(int initialCapacity)

Construit une liste de tableaux vide ayant la capacit spcifie.


Paramtres :

initialCapacity La capacit de stockage initiale de la liste de tableaux.

boolean add(T obj)

Ajoute un lment la fin de la liste de tableaux. Renvoie toujours true.


Paramtres :

obj

Llment ajouter.

int size()

Renvoie le nombre dlments actuellement contenus dans la liste de tableaux (cette valeur est
diffrente de la capacit. Bien entendu, elle est toujours infrieure ou gale la capacit de la
liste de tableaux).

void ensureCapacity(int capacity)

Vrifie que la liste de tableaux a la capacit ncessaire pour contenir le nombre dlments
donn, sans ncessiter la rallocation de son tableau de stockage interne.
Paramtres :

capacity

La capacit de stockage souhaite.

void trimToSize()

Rduit la capacit de stockage de la liste de tableaux sa taille actuelle.

Accder aux lments dune liste de tableaux


Malheureusement, rien nest gratuit ; les avantages que procure laccroissement automatique de
la taille dune liste de tableaux exigent une syntaxe plus complexe pour accder aux lments, car la
classe ArrayList ne fait pas partie du langage Java ; il sagit dune classe utilitaire tiers ajoute
la bibliothque standard.
Au lieu demployer la syntaxe [], bien pratique pour accder un lment dun tableau ou le modifier,
il faut appeler les mthodes get et set.
Par exemple, pour dfinir le ime lment, crivez
staff.set(i, harry);

ce qui est quivalent


a[i] = harry;

pour un tableau a (comme pour les tableaux, les valeurs dindex sont bases sur zro).
Pour obtenir un lment de liste de tableau, utilisez
Employee e = staff.get(i);

ce qui est quivalent :


Employee e = a[i];

Depuis le JDK 5.0, vous pouvez utiliser la boucle "for each" pour les listes de tableaux :
for (Employee e: staff)
// faire quelque chose avec e

Dans le code existant, la mme boucle scrirait :


for (int i = 0; i < staff.size(); i++)
{

Livre Java .book Page 211 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

211

Employee e = (Employee) staff.get(i);


faire quelque chose avec e
}

INFO
Avant le JDK 5.0, les classes gnriques nexistaient pas et la mthode get de la classe brute ArrayList navait
dautre choix que de renvoyer un Object. Les lments qui appelaient get devaient donc transtyper la valeur
renvoye sur le type souhait :
Employee e = (Employee) staff.get(i);

Le ArrayList brut est aussi un peu dangereux. Ses mthodes add et set acceptent des objets de nimporte quel
type. Un appel
staff.set(i, new Date());

se compile quasiment sans aucun avertissement et ne vous pose problme que lorsque vous rcuprez lobjet et que
vous essayez de le transtyper. Si vous utilisez plutt un ArrayList<Employee>, le compilateur dtecte cette erreur.

ASTUCE
Une petite astuce permet de profiter dun double avantage une croissance flexible et un accs pratique aux
lments. Crez dabord une liste de tableaux et ajoutez-y tous ses lments :
ArrayList<X> list = new ArrayList<X>();
while (. . .)
{
x = . . .;
list.add(x);
}

Utilisez ensuite la mthode toArray pour copier les lments dans un tableau :
X[] a = new X[list.size()];
list.toArray(a);

ATTENTION
Nappelez pas list.set(i, x) tant que la taille de la liste de tableaux nest pas suprieure i. Lexemple suivant
est incorrect :
ArrayList<Employee> list = new ArrayList<Employee>(100); // capacit 100, taille 0
list.set(0, x); // pas encore dlment 0

Appelez la mthode add au lieu de set pour remplir un tableau, et nemployez set que pour remplacer un lment
dj ajout.

Au lieu dajouter des lments la fin dune liste de tableaux, vous pouvez aussi les insrer au
milieu :
int n = staff.size() / 2;
staff.add(n, e);

Livre Java .book Page 212 Jeudi, 25. novembre 2004 3:04 15

212

Au cur de Java 2 - Notions fondamentales

Les lments situs aux emplacements n et suprieurs sont dcals pour faire place au nouvel
lment. Si la nouvelle taille de la liste de tableaux aprs linsertion excde la capacit, il est procd
une rallocation.
Il est galement possible de supprimer un lment au milieu dune liste de tableaux :
Employee e = staff.remove(n);

Les lments situs au-dessus de llment supprim sont dcals vers le bas, et la taille du tableau
est rduite de 1.
Linsertion et la suppression dlments ne sont pas trs efficaces. On ne sen proccupe gure dans
le cas de petites listes de tableaux. Si vous devez ajouter ou supprimer frquemment des lments au
milieu dune collection, il est prfrable dutiliser une liste lie. La programmation avec les listes
lies est tudie au Chapitre 2 du Volume 2.
LExemple 5.4 est une modification du programme EmployeeTest du Chapitre 4. Le Tableau
Employee[] est remplac par un ArrayList<Employee>. Remarquez les changements suivants :
m

Il nest pas ncessaire de spcifier la taille du tableau.

Vous appelez add pour ajouter autant dlments que vous voulez.

Vous utilisez size() au lieu de length pour compter le nombre dlments.

Vous accdez un lment laide de a.get(i) au lieu de a[i].

Exemple 5.4 : ArrayListTest.java


import java.util.*;
public class ArrayListTest
{
public static void main(String[] args)
{
// remplir le tableau staff avec trois objets Employee
ArrayList<employee> staff = new ArrayList<employee>();
staff.add(new Employee("Carl Cracker", 75000,
1987, 12, 15));
staff.add(new Employee("Harry Hacker", 50000,
1989, 10, 1));
staff.add(new Employee("Tony Tester", 40000,
1990, 3, 15));
// augmenter le salaire de tout le monde de 5%
for (Employee e: staff)
e.raiseSalary(5);
// afficher les informations concernant tous les objets Employee
for (Employee e: staff)
System.out.println("name="+e.getName()
+",salary="+e.getSalary()
+",hireDay="+e.getHireDay());
}
}

Livre Java .book Page 213 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

213

class Employee
{
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
private String name;
private double salary;
private Date hireDay;
}
java.util.ArrayList<T> 1.2

void set(int index, T obj)

Place une valeur dans la liste de tableau lindex spcifi, ce qui remplace le contenu prcdent.
Paramtres :
size() -1.

index

La position dinsertion, qui doit tre comprise entre 0 et

obj

La nouvelle valeur

T get(int index)

Rcupre la valeur stocke lindex spcifi.


Paramtres :

index

Lindex de llment rcuprer, qui doit tre compris entre


0 et size() -1.

Livre Java .book Page 214 Jeudi, 25. novembre 2004 3:04 15

214

Au cur de Java 2 - Notions fondamentales

void add(int index, T obj)

Place un nouvel lment la position spcifie, en dcalant les autres lments vers le haut.
Paramtres :

index

La position dinsertion, qui doit tre comprise entre 0 et


size() -1.

obj

Le nouvel lment.

T remove(int index)

Supprime un lment et dcale vers le bas les lments dindice suprieur. Llment supprim
est renvoy.
Paramtres :

index

Lindice de llment supprimer. Il doit tre compris entre


0 et size() -1.

Compatibilit entre les listes de tableaux brutes et tapes


Lorsque vous crivez un nouveau code avec JDK 5.0 et versions ultrieures, utilisez des paramtres
de type, comme ArrayList<Employee>, pour les listes de tableau. Vous pouvez toutefois avoir
besoin dinteragir avec le code existant, qui utilise le type ArrayList brut.
Supposons que vous disposiez de la classe existante suivante :
public class EmployeeDB
{
public void update(ArrayList list) { ... }
public ArrayList find(String query) { ... }
}

Vous pouvez transfrer une liste de tableau tape la mthode update sans autre transtypage :
ArrayList<Employee> staff = ...;
employeeDB.update(staff);

Lobjet staff est simplement transfr la mthode update.


ATTENTION
Mme si vous ne recevez pas derreur ou davertissement du compilateur, cet appel nest pas tout fait sr. La
mthode update pourrait ajouter des lments dans la liste de tableau de type Employee. Une exception
survient lors de la rcupration de ces lments. Ceci peut paratre effrayant mais, en y rflchissant bien, le
comportement est simplement le mme que celui avant le JDK 5.0. Lintgrit de la machine nest donc jamais
remise en cause. Dans ce cas, vous ne perdez pas en scurit, mais vous ne profitez pas non plus des vrifications
de compilation.

A linverse, lorsque vous attribuez un ArrayList brut un ArrayList tap, vous obtenez un avertissement :
ArrayList<Employee> result = employeeDB.find(query); // produit un avertissement

INFO
Pour voir le texte de lavertissement, vous devez procder la compilation avec loption -Xlint:unchecked.

Livre Java .book Page 215 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

215

Utiliser un transtypage ne permet pas de se dbarrasser de lavertissement :


ArrayList<Employee> result = (ArrayList<Employee>) employeeDB.find(query);
// produit un autre avertissement

Vous obtenez un autre avertissement, vous indiquant que le transtypage est erron.
Ceci est la consquence dune limitation assez malheureuse des types avec paramtres dans Java.
Pour des raisons de compatibilit, le compilateur traduit toutes les listes de tableaux tapes en objets
ArrayList bruts, aprs avoir vrifi que les rgles de type nont pas t violes. Dans un programme
en cours dexcution, toutes les listes de tableaux sont identiques, il ny a pas de paramtres de types
dans la machine virtuelle. Ainsi, les transtypages (ArrayList) et (ArrayList<Employee>) transportent des vrifications dexcution identiques.
Vous ne pouvez pas faire grand-chose face cette situation. Lorsque vous interagissez avec du code
existant, tudiez les avertissements du compilateur et persuadez-vous que les avertissements ne sont
pas srieux.

Enveloppes dobjets et autoboxing


Il est parfois ncessaire de convertir un type primitif comme int en un objet. Tous les types
primitifs ont une contrepartie sous forme de classe. Par exemple, il existe une classe Integer correspondant au type primitif int. Une classe de cette catgorie est gnralement appele classe enveloppe (object wrapper). Les classes enveloppes portent des noms correspondant aux types (en
anglais) : Integer, Long, Float, Double, Short, Byte, Character, Void et Boolean (les six
premires hritent de la superclasse Number). Les classes enveloppes sont inaltrables : vous ne
pouvez pas modifier une valeur enveloppe, une fois lenveloppe construite. Elles sont aussi final,
vous ne pouvez donc pas en faire des sous-classes.
Supposons que nous voulions travailler sur une liste dentiers. Malheureusement, le paramtre de
type entre les signes ne peut pas tre un type primitif. Il nest pas possible de former un ArrayList<int>. Cest ici que la classe enveloppe Integer fait son apparition. Vous pouvez dclarer une
liste de tableaux dobjets Integer:
ArrayList<Integer> list = new ArrayList<Integer>();

ATTENTION
Un ArrayList<Integer> est bien moins efficace quun tableau int[] car chaque valeur est enveloppe sparment dans un objet. Vous ne voudriez utiliser cette construction que pour les petites collections lorsque la commodit du programmeur est plus importante que lefficacit.

Une autre innovation du JDK 5.0 facilite lajout et la rcupration dlments de tableau. Lappel
list.add(3);

est automatiquement traduit en


list.add(new Integer(3));

Cette conversion est appele autoboxing.

Livre Java .book Page 216 Jeudi, 25. novembre 2004 3:04 15

216

Au cur de Java 2 - Notions fondamentales

INFO
Vous pourriez penser que lenveloppement automatique est plus cohrent, mais la mtaphore "boxing" (emballage)
provient du C#.

A linverse, lorsque vous attribuez un objet Integer une valeur int, il est automatiquement
dball (unboxing). En fait, le compilateur traduit
int n = list.get(i);

en
int n = list.get(i).intValue();

Les oprations dautoboxing et dunboxing fonctionnent mme avec les expressions arithmtiques.
Vous pouvez par exemple appliquer lopration dincrmentation en une rfrence denveloppe :
Integer n = 3;
n++;

Le compilateur insre automatiquement des instructions pour dballer lobjet, augmenter la valeur
du rsultat et le remballer.
Dans la plupart des cas, vous avez lillusion que les types primitifs et leurs enveloppes ne sont quun
seul et mme lment. Ils ne diffrent considrablement quen un seul point : lidentit. Comme
vous le savez, loprateur ==, appliqu des objets denveloppe, ne teste que si les objets ont des
emplacements mmoire identiques. La comparaison suivante chouerait donc probablement :
Integer a = 1000;
Integer b = 1000;
if (a == b) ...

Toutefois, une implmentation Java pourrait, si elle le choisit, envelopper des valeurs communes
dans des objets identiques, et la comparaison pourrait donc russir. Cette ambigut nest pas
souhaitable. La solution consiste appeler la mthode equals lorsque lon compare des objets
denveloppe.
INFO
La caractristique dautoboxing exige que boolean, byte, char = 127 et que short et int compris entre 128 et
127 soient envelopps dans des objets fixes. Par exemple, si a et b avaient t initialiss avec 100 dans lexemple
prcdent, la comparaison aurait russi.

Enfin, soulignons que le boxing et lunboxing sont proposs par le compilateur et non par la machine
virtuelle. Le compilateur insre les appels ncessaires lorsquil gnre les bytecodes dune classe.
La machine virtuelle excute simplement ces bytecodes.
Les classes enveloppe existent depuis le JDK 1.0 mais, avant le JDK 5.0, vous deviez insrer la
main le code pour le boxing et lunboxing.
Vous verrez souvent les enveloppes de nombres pour une autre raison. Les concepteurs de Java ont
dcouvert que les enveloppes constituent un endroit pratique pour y stocker certaines mthodes de
base, comme celles qui permettent de convertir des chanes de chiffres en nombres.

Livre Java .book Page 217 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

217

Pour transformer une chane en entier, vous devez utiliser linstruction suivante :
int x = Integer.parseInt(s);

Ceci na rien voir avec les objets Integer; parseInt est une mthode statique. Mais la classe
Integer constitue un bon endroit pour ly placer.
Les notes API montrent quelques mthodes parmi les plus importantes de la classe Integer. Les
autres classes de nombre implmentent les mthodes correspondantes.
ATTENTION
Certains pensent, tort, que les classes enveloppes peuvent tre employes pour implmenter des mthodes capables de modifier les paramtres numriques. Nous avons vu au Chapitre 4 quil tait impossible dcrire une mthode
Java qui incrmente un paramtre entier, car les paramtres des mthodes Java sont toujours passs par valeur.
public static void triple(int x) // ne marchera pas
{
X = 3 * x; // modifie la variable locale
}

Mais ne pourrait-on pas contourner ce problme en substituant un Integer un int?


public static void triple(Integer x) // ne marchera pas
{
. . .
}

Le problme vient du fait que les objets Integer sont inaltrables : linformation contenue dans lenveloppe ne
peut pas changer. Vous ne pouvez pas employer les classes enveloppes pour crer une mthode qui modifie les paramtres numriques.

INFO
Si vous dsirez crire une mthode qui modifie des paramtres numriques, vous pouvez utiliser un des types holder
dfinis dans le package org.omg.CORBA. Il existe des types IntegerHolder, BooleanHolder, etc. Chaque type
holder a un champ public value grce auquel vous pouvez accder la valeur stocke :
public static void triple(IntHolder x)
{
x.value = 3 * x.value;
}

java.lang.Integer 1.0

int intValue()

Renvoie la valeur de cet objet Integer dans un rsultat de type int (surcharge la mthode
intValue de la classe Number).

static String toString(int i)

Renvoie un nouvel objet chane reprsentant le nombre, en base 10.

static String toString(int i, int radix)

Permet de renvoyer une reprsentation du nombre i dans la base spcifie par le paramtre
radix.

Livre Java .book Page 218 Jeudi, 25. novembre 2004 3:04 15

218

Au cur de Java 2 - Notions fondamentales

static int parseInt(String s)

Renvoie la valeur dentier de la chane s, si elle reprsente un nombre entier en base 10.

static int parseInt(String s, int radix)

Renvoie la valeur dentier de la chane s, si elle reprsente un nombre entier exprim dans la
base spcifie par le paramtre radix.

static Integer valueOf(String s)

Renvoie un nouvel objet Integer initialis avec la valeur de la chane spcifie, si celle-ci reprsente un nombre entier en base 10.

static Integer valueOf(String s, int radix)

Renvoie un nouvel objet Integer initialis avec la valeur de la chane s, si celle-ci reprsente un
nombre entier exprim dans la base spcifie par le paramtre radix.
java.text.NumberFormat 1.1

Number parse(String s)

Renvoie la valeur numrique, en supposant que la chane spcifie reprsente un nombre.

Mthodes ayant un nombre variable de paramtres


Avant JDK 5.0, chaque mthode Java disposait dun nombre fixe de paramtres. Il est toutefois
maintenant possible de fournir des mthodes qui peuvent tre appeles avec un nombre variable de
paramtres (quelquefois appels mthodes "varargs").
Vous avez dj vu une telle mthode, la mthode printf. Par exemple, les appels
System.out.printf("%d", n);

et
System.out.printf("%d %s", n, "widgets");

appellent tous deux la mme mthode, mme si lun a deux paramtres et lautre, trois.
La mthode printf est dfinie comme ceci :
public class PrintStream
{
public PrintStream printf(String fmt, Object... args)
{ return format(fmt, args); }
}

Ici, lellipse... fait partie du code Java. Elle montre que la mthode peut recevoir un nombre arbitraire dobjets (en plus du paramtre fmt).
La mthode printf reoit en fait deux paramtres, la chane format et un tableau Object[] qui
contient tous les autres paramtres (si lappelant fournit des entiers ou autres valeurs de type
primitif, lautoboxing les transforme en objets). Elle dispose maintenant de la tche non enviable
danalyser la chane fmt et de se conformer au spcificateur du format ith avec la valeur
args[i].
Autrement dit, pour limplmenteur de printf, le type de paramtre Object... est exactement le
mme que Object[].

Livre Java .book Page 219 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

219

Le compilateur doit transformer chaque appel printf, en regroupant les paramtres dans un
tableau et en procdant lautoboxing en fonction des besoins :
System.out.printf("%d %d", new Object[] { new Integer(d), "widgets" } );

Vous pouvez dfinir vos propres mthodes avec des paramtres variables et spcifier tout type pour
les paramtres, mme un type primitif. Voici un exemple simple : une fonction qui calcule le maximum
dun nombre variable de valeurs :
public static double max(double... values)
{
double largest = Double.MIN_VALUE;
for (double v: values) if (v > largest) largest = v;
return largest;
}

Appelez simplement la fonction comme ceci :


double m = max(3.1, 40.4, -5);

Le compilateur transfre un nouveau double[] { 3.1, 40.4, -5 } la fonction max.


INFO
Le transfert dun tableau comme dernier paramtre dune mthode avec des paramtres variables est autoris, par
exemple :
System.out.printf("%d %s", new Object[] { new Integer(1), "widgets" } );

Vous pouvez donc redfinir une fonction existante dont le dernier paramtre est un tableau par une mthode disposant de paramtres variables, sans casser un code existant. MessageFormat.format a, par exemple, t amlior dans
ce sens dans le JDK 5.0.

Rflexion
La bibliothque de rflexion constitue une bote outils riche et labore pour crire des programmes qui manipulent dynamiquement du code Java. Cette fonctionnalit est trs largement utilise
dans JavaBeans, larchitecture des composants Java (voir le Volume 2 pour en savoir plus sur JavaBeans). Au moyen de la rflexion, Java est capable de supporter des outils comme ceux auxquels les
utilisateurs de Visual Basic sont habitus. Prcisment, lorsque de nouvelles classes sont ajoutes au
moment de la conception ou de lexcution, des outils de dveloppement dapplication rapide
peuvent se renseigner dynamiquement sur les capacits des classes qui ont t ajoutes.
Un programme qui peut analyser les capacits des classes est appel rflecteur. Le mcanisme de
rflexion est extrmement puissant. Comme vous le montrent les sections suivantes, il peut tre
employ pour :
m

analyser les capacits des classes au moment de lexcution ;

tudier les objets au moment de lexcution, par exemple pour crire une seule mthode
toString qui fonctionne pour toutes les classes ;

implmenter un code gnrique pour la manipulation des tableaux ;

profiter des objets Method qui se comportent comme les pointeurs de fonction des langages tels
que C++.

Livre Java .book Page 220 Jeudi, 25. novembre 2004 3:04 15

220

Au cur de Java 2 - Notions fondamentales

La rflexion est un mcanisme puissant et complexe ; il intresse toutefois principalement les


concepteurs doutils, et non les programmeurs dapplications. Si vous vous intressez la programmation des applications plutt quaux outils pour les programmeurs Java, vous pouvez ignorer sans
problme le reste de ce chapitre et y revenir par la suite.

La classe Class
Lorsque le programme est lanc, le systme dexcution de Java gre, pour tous les objets, ce que
lon appelle "lidentification de type lexcution". Cette information mmorise la classe laquelle
chaque objet appartient. Linformation de type au moment de lexcution est employe par la
machine virtuelle pour slectionner les mthodes correctes excuter.
Vous pouvez accder cette information en travaillant avec une classe Java particulire, baptise
singulirement Class. La mthode getClass() de la classe Object renvoie une instance de type
Class:
Employee e;
. . .
Class cl = e.getClass();

Tout comme un objet Employee dcrit les proprits dun employ particulier, un objet Class dcrit
les proprits dune classe particulire. La mthode la plus utilise de Class est sans conteste
getName, qui renvoie le nom de la classe. Par exemple, linstruction
System.out.println(e.getClass().getName()+" "+e.getName());

affiche
Employee Harry Hacker

si e est un employ ou
Manager Harry Hacker

si e est un directeur.
Vous pouvez aussi obtenir un objet Class correspondant une chane, laide de la mthode statique
forName:
String className = "java.util.Date";
Class cl = Class.forName(className);

Cette technique est utilise si le nom de classe est stock dans une chane qui peut changer lexcution. Cela fonctionne lorsque className est bien le nom dune classe ou dune interface. Dans le cas
contraire, la mthode forName lance une exception vrifie. Consultez le texte encadr plus loin pour
voir comment fournir un gestionnaire dexception lorsque vous utilisez cette mthode.
ASTUCE
Au dmarrage, la classe contenant votre mthode main est charge. Elle charge toutes les classes dont elle a besoin.
Chacune de ces classes charge les classes qui lui sont ncessaires, et ainsi de suite. Cela peut demander un certain
temps pour une application importante, ce qui est frustrant pour lutilisateur. Vous pouvez donner aux utilisateurs
de votre programme lillusion dun dmarrage plus rapide, laide de lastuce suivante. Assurez-vous que la classe
contenant la mthode main ne fait pas explicitement rfrence aux autres classes. Affichez dabord un cran splash,
puis forcez manuellement le chargement des autres classes en appelant Class.forName.

Livre Java .book Page 221 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

221

Une troisime technique emploie un raccourci pratique pour obtenir un objet de type Class. En
effet, si T est dun type Java quelconque, alors T.class reprsente lobjet classe correspondant.
Voici quelques exemples :
Class cl1 = Date.class; // si vous importez java.util.*;
Class cl2 = int.class;
Class cl3 = Double[].class;

Remarquez quun objet Class dsigne en ralit un type, qui nest pas ncessairement une classe.
Par exemple, int nest pas une classe, mais int.class est pourtant un objet du type Class.
INFO
Depuis le JDK 5.0, la classe Class possde des paramtres. Par exemple, Employee.class est de type
Class<Employee>. Nous ne traiterons pas de ce problme car il compliquerait encore un concept dj abstrait.
Dans un but pratique, vous pouvez ignorer le paramtre de type et travailler avec le type Class brut. Voyez le Chapitre 13 pour en savoir plus sur ce problme.

ATTENTION
Pour des raisons historiques, la mthode getName renvoie des noms tranges pour les types tableaux :

Double[].class.getName() returns "[Ljava.lang.Double;".


int[].class.getName() returns "[I".

La machine virtuelle gre un unique objet Class pour chaque type. Vous pouvez donc utiliser
loprateur == pour comparer les objets class, par exemple :
if (e.getClass() == Employee.class) . . .

Interception dexceptions
La gestion des exceptions est vue en dtail au Chapitre 11 mais, en attendant, vous pouvez
rencontrer des cas o les mthodes menacent de lancer des exceptions.
Lorsquune erreur se produit au moment de lexcution, un programme peut "lancer une exception". Laction de lancer une exception est plus souple que de terminer le programme, car vous
pouvez fournir un gestionnaire qui "intercepte" lexception et la traite.
Si vous ne fournissez pas de gestionnaire, le programme se termine pourtant et affiche la
console un message donnant le type de lexception. Vous avez peut-tre dj vu un compte rendu
dexception si vous employez tort une rfrence null ou que vous dpassiez les limites dun
tableau.
Il existe deux sortes dexceptions : vrifies et non vrifies (checked/unchecked). Dans le cas
dexceptions vrifies, le compilateur vrifie que vous avez fourni un gestionnaire. Cependant, de
nombreuses exceptions communes, telle quune tentative daccder une rfrence null, ne sont
pas vrifies. Le compilateur ne vrifie pas si vous fournissez un gestionnaire pour ces erreurs
aprs tout, vous tes cens mobiliser votre nergie pour viter ces erreurs plutt que de coder des
gestionnaires pour les traiter.

Livre Java .book Page 222 Jeudi, 25. novembre 2004 3:04 15

222

Au cur de Java 2 - Notions fondamentales

Mais toutes les erreurs ne sont pas vitables. Si une exception peut se produire en dpit de vos
efforts, le compilateur sattendra ce que vous fournissiez un gestionnaire. Class.forName est un
exemple de mthode qui dclenche une exception vrifie. Vous tudierez, au Chapitre 11,
plusieurs stratgies de gestion dexception. Pour linstant, nous nous contenterons de vous indiquer
limplmentation du gestionnaire le plus simple.
Placez une ou plusieurs mthodes pouvant lancer des exceptions vrifies, dans un bloc dinstructions
try, puis fournissez le code du gestionnaire dans la clause catch.
try
{
instructions pouvant dclencher des exceptions
}
catch(Exception e)
{
action du gestionnaire
}
En voici un exemple :
try
{ String name = . . .; // extraire le nom de classe
Class cl = Class.forName(name); // peut lancer une exception
. . . // faire quelque chose avec cl
}
catch(Exception e)
{
e.printStackTrace();
}
Si le nom de classe nexiste pas, le reste du code dans le bloc try est saut, et le programme se
poursuit la clause catch (ici, nous imprimons une trace de pile laide de la mthode printStackTrace de la classe Throwable qui est la superclasse de la classe Exception). Si aucune des
mthodes dans le bloc try ne dclenche dexception, le code du gestionnaire dans la clause catch
est saut.
Vous navez besoin de fournir de gestionnaire dexception que pour les exceptions vrifies. Il est
facile de dterminer les mthodes qui lancent des exceptions vrifies le compilateur protestera
quand vous appellerez une mthode menaant de lancer une exception vrifie et que ne fournirez
pas de gestionnaire.

Il existe une autre mthode utile qui permet de crer une instance de classe la vole. Cette mthode
se nomme, assez naturellement, newInstance(). Par exemple,
e.getClass().newInstance();

cre une nouvelle instance du mme type de classe que e. La mthode newInstance appelle le
constructeur par dfaut (celui qui ne reoit aucun paramtre) pour initialiser lobjet nouvellement
cr. Une exception est dclenche si la classe ne dispose pas de constructeur par dfaut.
Une combinaison de forName et de newInstance permet de crer un objet partir dun nom de
classe stock dans une chane :
String s = "java.util.Date";
Object m = Class.forName(s).newInstance();

Livre Java .book Page 223 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

223

INFO
Vous ne pourrez pas employer cette technique si vous devez fournir des paramtres au constructeur dune classe que
vous souhaitez instancier la vole. Vous devrez recourir la mthode newInstance de la classe Constructor (il
sagit dune des classes du package java.lang.reflect, dont nous parlerons dans la section suivante).

INFO C++
La mthode newInstance correspond un constructeur virtuel en C++. Cependant, la construction virtuelle en C++
nest pas une caractristique du langage, mais une simple tournure idiomatique qui doit tre prise en charge par une
bibliothque spcialise. Class est comparable la classe type_info de C++, et la mthode getClass quivaut
loprateur typeid. Nanmoins, la classe Class de Java est plus souple que type_info qui peut seulement fournir
le nom dun type, et non crer de nouveaux objets de ce type.

java.lang.Class 1.0

static Class forName(String className)

Renvoie lobjet Class qui reprsente la classe ayant pour nom className.

Object newInstance()

Renvoie une nouvelle instance de cette classe.


java.lang.reflect.Constructor 1.1

Object newInstance(Object[] args)

Construit une nouvelle instance de la classe dclarante du constructeur.


Paramtres :

args

Les paramtres fournis au constructeur. Voir la section


relative la rflexion pour plus dinformations sur la faon
de fournir les paramtres.

java.lang.Throwable 1.0

void printStackTrace()

Affiche lobjet Throwable et la trace de la pile dans lunit derreur standard.

La rflexion pour analyser les caractristiques dune classe


Nous vous proposons un bref aperu des lments les plus importants du mcanisme de rflexion,
car il permet dexaminer la structure dune classe.
Les trois classes Field, Method et Constructor, qui se trouvent dans le package java.lang.reflect,
dcrivent respectivement les champs, les mthodes et les constructeurs dune classe. Ces trois classes disposent dune mthode getName qui renvoie le nom de llment. La classe Field possde une
mthode getType renvoyant un objet, de type Class, qui dcrit le type du champ. Les classes
Method et Constructor possdent des mthodes permettant dobtenir les types des paramtres, et la
classe Method signale aussi le type de retour. Les trois classes possdent galement une mthode
appele getModifiers: elle renvoie un entier dont les bits sont utiliss comme smaphores pour
dcrire les modificateurs spcifis, tels que public ou static. Vous pouvez alors utiliser les mthodes statiques de la classe Modifier du package java.lang.reflect pour analyser les entiers
renvoys par getModifiers. Par exemple, il existe des mthodes telles que isPublic, isPrivate ou
isFinal pour dterminer si un constructeur ou une mthode a t dclare public, private ou final.

Livre Java .book Page 224 Jeudi, 25. novembre 2004 3:04 15

224

Au cur de Java 2 - Notions fondamentales

Il vous suffit dappeler la mthode approprie de Modifier et de lutiliser sur lentier renvoy par
getModifiers. Il est galement possible demployer Modifier.toString pour afficher les modificateurs.
Les mthodes getFields, getMethods et getConstructors de la classe Class renvoient dans
des tableaux les lments publics, les mthodes et les constructeurs grs par la classe. Ces
lments sont des objets de la classe correspondante de java.lang.reflect. Cela inclut les
membres publics des superclasses. Les mthodes getDeclaredFields, getDeclaredMethods et
getDeclaredConstructors de Class renvoient des tableaux constitus de tous les champs,
mthodes et constructeurs dclars dans la classe, y compris les membres privs et protgs,
mais pas les membres des superclasses.
LExemple 5.5 explique comment afficher toutes les informations relatives une classe. Le
programme vous demande le nom dune classe, puis affiche la signature de chaque mthode et de
chaque constructeur ainsi que le nom de chaque champ de donnes de la classe en question. Par
exemple, si vous tapez :
java.lang.Double

Le programme affiche :
class java.lang.Double extends java.lang.Number
{
public java.lang.Double(java.lang.String);
public java.lang.Double(double);
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public

int hashCode();
int compareTo(java.lang.Object);
int compareTo(java.lang.Double);
boolean equals(java.lang.Object);
java.lang.String toString();
static java.lang.String toString(double);
static java.lang.Double valueOf(java.lang.String);
static boolean isNaN(double);
boolean isNaN();
static boolean isInfinite(double);
boolean isInfinite();
byte byteValue();
short shortValue();
int intValue();
long longValue();
float floatValue();
double doubleValue();
static double parseDouble(java.lang.String);
static native long doubleToLongBits(double);
static native long doubleToRawLongBits(double);
static native double longBitsToDouble(long);

public static final double POSITIVE_INFINITY;


public static final double NEGATIVE_INFINITY;
public static final double NaN;
public static final double MAX_VALUE;
public static final double MIN_VALUE;
public static final java.lang.Class TYPE;
private double value;
private static final long serialVersionUID;
}

Livre Java .book Page 225 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

225

Ce qui est remarquable dans ce programme, cest quil peut analyser toute classe apte tre charge
par linterprteur, et pas seulement les classes disponibles au moment o le programme est compil.
Nous le rutiliserons au chapitre suivant pour examiner les classes internes gnres automatiquement
par le compilateur Java.
Exemple 5.5 : ReflectionTest.java
import java.util.*;
import java.lang.reflect.*;
public class ReflectionTest
{
public static void main(String[] args)
{
/* lire le nom de classe dans les args de ligne de commande
ou lentre de lutilisateur */
String name;
if (args.length > 0)
name = args[0];
else
{
Scanner in = new Scanner(System.in);
System.out.println("Enter class Name ("e.g. java.util.Date): ");
name = in.next();
}
try
{
/* afficher le nom de classe et de superclasse
(if!= Object) */
Class cl = Class.forName(name);
Class supercl = cl.getSuperclass();
System.out.print("class "+name);
if (supercl!= null && supercl!= Object.class)
System.out.print(" extends "+supercl.getName());
System.out.print("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
}
catch(ClassNotFoundException e) { e.printStackTrace(); }
System.exit(0);
}
/**
affiche tous les constructeurs dune classe
@param cl Une classe
*/
public static void printConstructors(Class cl)
{
Constructor[] constructors = cl.getDeclaredConstructors();
for (Constructor c: constructors)
{

Livre Java .book Page 226 Jeudi, 25. novembre 2004 3:04 15

226

Au cur de Java 2 - Notions fondamentales

String name = c.getName();


System.out.print("
" + Modifier.toString(c.getModifiers()));
System.out.print(" "+name+"(");
// afficher les types des paramtres
Class[] paramTypes = c.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
/**
imprime toutes les mthodes dune classe
@param cl Une classe
*/
public static void printMethods(Class cl)
{
Method[] methods = cl.getDeclaredMethods();
for (Method m: methods)
Class retType = m.getReturnType();
String name = m.getName();
/* affiche les modificateurs, le type renvoy
et le nom de la mthode */
System.out.print("
" + Modifier.toString(m.getModifiers()));
System.out.print("
"+retType.getName()+" "+name
+"(");
// imprime les types des paramtres
Class[] paramTypes = m.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
/**
Affiche tous les champs dune classe
@param cl Une classe
*/
public static void printFields(Class cl)
{
Field[] fields = cl.getDeclaredFields();
for (Field f: fields)
Class type = f.getType();
String name = f.getName();

Livre Java .book Page 227 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

System.out.print("
System.out.println("
+";");

227

" + Modifier.toString(f.getModifiers()));
" +type.getName()+" "+name

}
}
}
java.lang.Class 1.0
Field[] getFields() 1.1
Field[] getDeclaredFields() 1.1

La mthode getFields renvoie un tableau contenant les objets Field reprsentant les champs
publics de cette classe ou de ses superclasses. La mthode getDeclaredFields renvoie un
tableau dobjets Field pour tous les champs de cette classe. Ces mthodes renvoient un tableau
de longueur 0 sil ny a pas de champ correspondant ou si lobjet Class reprsente un type
primitif ou un type tableau.

Method[] getMethods 1.1()


Method[] getDeclaredMethods() 1.1

Renvoient un tableau qui contient des objets Method: getMethods renvoie des mthodes publiques et inclut des mthodes hrites ; getDeclaredMethods renvoie toutes les mthodes de cette
classe ou interface mais ninclut pas les mthodes hrites.

Constructor[] getConstructors 1.1 ()


Constructor[] getDeclaredConstructors() 1.1

Renvoient un tableau dobjets Constructor reprsentant tous les constructeurs publics (avec
getConstructors) ou tous les constructeurs (avec getDeclaredConstructors) de la classe
dsigne par cet objet Class.
java.lang.reflect.Field 1.1
java.lang.reflect.Method 1.1
java.lang.reflect.Constructor 1.1
Class getDeclaringClass()

Renvoie lobjet Class de la classe qui dfinit ce constructeur, cette mthode ou ce champ.

Class[] getExceptionTypes()

Dans les classes Constructor et Method, renvoie un tableau dobjets Class qui reprsentent les
types dexceptions dclenches par la mthode.

int getModifiers()

Renvoie un entier dcrivant les modificateurs du constructeur, de la mthode ou du champ. Utilisez


les mthodes de la classe Modifier pour analyser le rsultat.

String getName()

Renvoie une chane qui donne le nom du constructeur, de la mthode ou du champ.

Class[] getParameterTypes()

Dans les classes Constructor et Method, renvoie un tableau dobjets Class qui reprsentent les
types des paramtres.

Class getReturnType() (dans les classes Method)

Renvoie un objet Class qui reprsente le type de retour.

Livre Java .book Page 228 Jeudi, 25. novembre 2004 3:04 15

228

Au cur de Java 2 - Notions fondamentales

java.lang.reflect.Modifier 1.1

static String toString(int modifiers)

Renvoie une chane avec les modificateurs correspondant aux bits dfinis dans modifiers.

static boolean isAbstract(int modifiers)


static boolean isFinal(int modifiers)
static boolean isInterface(int modifiers)
static boolean isNative(int modifiers)
static boolean isPrivate(int modifiers)
static boolean isProtected(int modifiers)
static boolean isPublic(int modifiers)
static boolean isStatic(int modifiers)
static boolean isStrict(int modifiers)
static boolean isSynchronized(int modifiers)
static boolean isVolatile(int modifiers)

Ces mthodes testent le bit dans la valeur modifiers qui correspond llment modificateur du
nom de la mthode.

La rflexion pour lanalyse des objets lexcution


Dans la section prcdente, nous avons vu comment trouver le nom et le type des champs de
nimporte quel objet :
m

obtenir lobjet Class correspondant ;

appeler getDeclaredFields sur lobjet Class.

Dans cette section, nous allons franchir une tape supplmentaire et tudier le contenu des champs.
Bien entendu, il est facile de lire le contenu dun champ spcifique dun objet dont le nom et le type
sont connus lors de lcriture du programme. Mais la rflexion nous permet de lire les champs des
objets qui ntaient pas connus au moment de la compilation.
A cet gard, la mthode essentielle est la mthode get de la classe Field. Si f est un objet de type
Field (obtenu par exemple grce getDeclaredFields) et obj un objet de la classe dont f est un
champ, alors f.get(obj) renvoie un objet dont la valeur est la valeur courante du champ de obj.
Tout cela peut paratre un peu abstrait ; nous allons donc prendre un exemple :
Employee harry = new Employee("Harry Hacker", 35000,
10, 1, 1989);
Class cl = harry.getClass();
// lobjet class reprsentant Employee
Field f = cl.getDeclaredField("name");
// le champ name de la classe Employee
Object v = f.get(harry);
/* la valeur du champ name de lobjet harry
cest--dire lobjet String "Harry Hacker" */

En fait, ce code pose un problme. Comme le champ name est un champ priv, la mthode get
dclenche une exception IllegalAccessException (Exception pour accs interdit). Cette mthode
ne peut tre employe que pour obtenir les valeurs des champs accessibles. Le mcanisme de scurit de Java vous permet de connatre les champs dun objet, mais pas de lire la valeur de ces champs
si vous navez pas une autorisation daccs.

Livre Java .book Page 229 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

229

Par dfaut, le mcanisme de rflexion respecte le contrle des accs. Nanmoins, si un


programme Java nest pas contrl par un gestionnaire de scurit qui le lui interdit, il peut outrepasser son droit daccs. Pour cela, il faut invoquer la mthode setAccessible dun objet
Field, Method ou Constructor:
f.setAccessible(true);
// les appels f.get(harry) sont maintenant autoriss

La mthode setAccessible se trouve dans la classe AccessibleObject, qui est la superclasse


commune des classes Field, Method et Constructor. Cette fonctionnalit est destine au dbogage,
au stockage permanent et des mcanismes similaires. Nous lemploierons pour tudier une
mthode toString gnrique.
La mthode get pose un second problme. Le champ name est de type String, il est donc possible
de rcuprer la valeur en tant que Object. Mais supposons que nous dsirions tudier le champ
salary. Celui-ci est de type double, et les nombres ne sont pas des objets en Java. Il existe deux
solutions. La premire consiste utiliser la mthode getDouble de la classe Field; la seconde est
un appel get, car le mcanisme de rflexion enveloppe automatiquement la valeur du champ dans
la classe enveloppe approprie, en loccurrence, Double.
Bien entendu, il est possible de modifier les valeurs obtenues. Lappel f.set(obj, value) affecte
une nouvelle valeur au champ de lobjet obj reprsent par f.
LExemple 5.6 montre comment crire une mthode toString gnrique capable de fonctionner
avec nimporte quelle classe. Elle appelle dabord getDeclaredField pour obtenir tous les champs
de donnes. Elle utilise ensuite la mthode setAccessible pour rendre tous ces champs accessibles,
puis elle rcupre le nom et la valeur de chaque champ. Lexemple 5.6 convertit chaque valeur en
chane par un appel sa propre mthode toString:
class ObjectAnalyzer
{
public String toString(Object obj)
{
Class cl = obj.getClass();
...
String r = cl.getName();
/* inspecter les champs de cette classe et de
toutes les superclasses */
do
{
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
// extraire les noms et valeurs de tous les champs
for (Field f: fields)
{
if (!Modifier.isStatic(f.getModifiers()))
{
if (!r.endsWith("[")) r += ","
r += f.getName()+"=";
try
{
Object val = f.get(obj);
r += val.toString(val);
}

Livre Java .book Page 230 Jeudi, 25. novembre 2004 3:04 15

230

Au cur de Java 2 - Notions fondamentales

catch (Exception e) { e.printStackTrace(); }


}
}
r += "]";
cl = cl.getSuperclass();
}
while (cl!= Object.class);
return r;
}
. . .
}

La totalit du code de lExemple 5.6 doit traiter deux complexits. Les cycles de rfrence pourraient entraner une rcursion infinie. ObjectAnalyzer suit donc la trace des objets qui ont dj t
visits. De mme, pour regarder dans les tableaux, vous devez adopter une approche diffrente. Vous
en saurez plus dans la section suivante.
Cette mthode toString peut tre employe pour dissquer nimporte quel objet. Par exemple,
lappel
ArrayList<Integer> squares = new ArrayList<Integer>();
for (int i = 1; i <= 5; i++) squares.add(i * i);
System.out.println(new ObjectAnalyzer().toString(squares));

produit limpression
java.util.ArrayList[elementData=class
java.lang.Object[]{java.lang.Integer[value=1][][],
java.lang.Integer[value=4][][],
java.lang.Integer[value=9][][],java.lang.Integer[value=16][][],
java.lang.Integer[value=25][][],
null,null,null,null,null},size=5][modCount=5][][]

La mthode gnrique toString est utilise dans lExemple 5.6 pour implmenter les mthodes
toString de vos propres classes, comme ceci :
public String toString()
{
return ObjectAnalyzer.toString(this);
}

Il sagit dune technique sre pour crer une mthode toString, qui pourra vous tre utile dans vos
programmes.
Exemple 5.6 : ObjectAnalyzerTest.java
import java.lang.reflect.*;
import java.util.*;
import java.text.*;
public class ObjectAnalyzerTest
{
public static void main(String[] args)
{
ArrayList<Integer> squares = new ArrayList<Integer>();
for (int i = 1; i <= 5; i++) squares.add(i * i);
System.out.println(new ObjectAnalyzer().toString(squares));
}
}

Livre Java .book Page 231 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

class ObjectAnalyzer
{
/**
Convertit un objet en une reprsentation chane
qui liste tous les champs.
@param obj Un objet
@return une chane avec le nom de classe de lobjet
et tous les noms et valeurs de champs
*/
public String toString(Object obj)
{
if (obj == null) return "null";
if (visited.contains(obj)) return "...";
visited.add(obj);
Class cl = obj.getClass();
if (cl == String.class) return (String) obj;
if (cl.isArray())
{
String r = cl.getComponentType() + "[]{";
for (int i = 0; i < Array.getLength(obj); i++)
{
if (i > 0) r += ",";
Object val = Array.get(obj, i);
if (cl.getComponentType().isPrimitive()) r += val;
else r += toString(val);
}
return r + "}";
}
String r = cl.getName();
/* inspecter les champs de cette classe et de
toutes les superclasses */
do
{
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
// extraire les noms et valeurs de tous les champs
for (Field f: fields)
{
if (!Modifier.isStatic(f.getModifiers()))
{
if (!r.endsWith("[")) r += ",";
r += f.getName()+"=";
try
{
Class t = f.getType();
Object val = f.get(obj);
if (t.isPrimitive()) r += val;
else r += toString(val);
}
catch (Exception e) { e.printStackTrace(); }
}
r += "]";
cl = cl.getSuperclass();
}

231

Livre Java .book Page 232 Jeudi, 25. novembre 2004 3:04 15

232

Au cur de Java 2 - Notions fondamentales

while (cl!= null);


return r;
}
private ArrayList<Object> visited = new ArrayList<Object>();
}
java.lang.reflect.AccessibleObject 1.2

void setAccessible(boolean flag)

Dfinit lindicateur daccessibilit de lobjet rflexion. La valeur true signifie que la vrification
daccs de Java est invalide et que les proprits prives de lobjet peuvent tre lues et affectes.

boolean isAccessible()

Rcupre la valeur de lindicateur daccessibilit de cet objet rflexion.

static void setAccessible(AccessibleObject[] array,boolean flag)

Permet de spcifier ltat de lindicateur daccessibilit pour un tableau dobjets.


java.lang.Class 1.1

Field getField(String name)


Field[] getFields()

Rcupre le champ public avec le nom donn ou un tableau de tous les champs.

Field getDeclaredField(String name)


Field[] getDeclaredFields()

Rcupre le champ qui est dclar dans cette classe avec le nom donn ou un tableau de tous les
champs.
java.lang.reflect.Field 1.1

Object get(Object obj)

Rcupre la valeur du champ dcrit par cet objet Field dans lobjet obj.

void set(Object obj, Object newValue)

Dfinit le champ dcrit par cet objet Field dans lobjet obj avec une nouvelle valeur.

La rflexion pour crer un tableau gnrique


La classe Array du package java.lang.reflect permet de crer des tableaux dynamiques.
Lorsquon utilise cette caractristique avec la mthode arrayCopy, dcrite au Chapitre 3, il est
possible dtendre dynamiquement la taille dun tableau existant tout en en prservant le contenu
actuel.
Le problme que nous souhaitons rsoudre est assez typique. Supposons que nous disposions dun
tableau dun type quelconque qui est satur et que nous voulions lagrandir. Comme nous
sommes fatigus dcrire systmatiquement le code traditionnel ("augmenter le bloc mmoire et
recopier"), nous dcidons dcrire une mthode gnrique permettant dagrandir un tableau :
Employee[] a = new Employee[100];
. . .
// le tableau est satur
a = (Employee[])arrayGrow(a);

Livre Java .book Page 233 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

233

Comment crire cette mthode gnrique ? Heureusement, un Tableau Employee[] peut tre
converti en tableau Object[]. Cela sannonce bien. Voici une premire mouture de notre mthode
gnrique. Nous augmentons le tableau de 10 % + 10 lments (car une simple augmentation de
10 % ne suffirait pas pour de petits tableaux) :
static Object[] badArrayGrow(Object[] a) // sans intrt
{
int newLength = a.length * 11 / 10+10;
Object[] newArray = new Object[newLength];
System.arraycopy(a, 0, newArray, 0, a.length);
return newArray;
}

La question se pose nanmoins de lutilisation du tableau qui en rsulte. Le type de tableau renvoy
par ce code est un tableau dobjets (Object[]), il est en fait cr par cette instruction :
new Object[newLength]

Un tableau dobjets ne peut pas tre transtyp en tableau demploys (Employee[]). Java dclencherait une exception de transtypage ClassCastException lexcution. Comme nous lavons dj
expliqu, un tableau Java mmorise le type de ses lments, cest--dire le type dlment utilis
dans lexpression new lors de la cration du tableau. Il est valide de transtyper temporairement un
Tableau Employee[] en tableau Object[], et de le retranstyper ensuite dans son type dorigine.
Mais un tableau qui a t initialement cr en tant que tableau Object[] ne peut jamais tre transtyp en Tableau Employee[]. Pour dvelopper ce genre de tableau gnrique, nous devons tre capables de crer un nouveau tableau ayant le mme type que le tableau original. Pour cela, il nous faut
exploiter les mthodes de la classe Array du package java.lang.reflect. La cl de lopration est
la mthode newInstance de la classe Array, qui construit un nouveau tableau. Il faut indiquer
cette mthode, en paramtres, le type des lments et la nouvelle taille dsire :
Object newArray = Array.newInstance(componentType, newLength);

Pour parvenir nos fins, il nous faut connatre la taille (ou longueur) et le type du nouveau tableau.
Cette taille est obtenue par un appel Array.getLength(a). La mthode statique getLength
renvoie la taille de nimporte quel tableau. Pour obtenir le type, il faut suivre ces tapes :
1. Obtenir dabord lobjet classe de a.
2. Confirmer quil sagit bien dun tableau.
3. Utiliser ensuite la mthode getComponentType de la classe Class (qui nest dfinie que pour les
objets classe reprsentant des tableaux) afin de connatre le vritable type du tableau.
On peut se demander pourquoi getLength est une mthode de Array et getComponentType une
mthode de Class. Ce choix a sans doute paru appropri aux concepteurs.
Voici la nouvelle version de notre mthode :
static Object goodArrayGrow(Object a) // utile
{
Class cl = a.getClass();
if (!cl.isArray()) return null;
Class componentType = cl.getComponentType();
int length = Array.getLength(a);
int newLength = length * 11 / 10+10;
Object newArray = Array.newInstance(componentType,
newLength);

Livre Java .book Page 234 Jeudi, 25. novembre 2004 3:04 15

234

Au cur de Java 2 - Notions fondamentales

System.arraycopy(a, 0, newArray, 0, length);


return newArray;
}

Remarquez que notre mthode arrayGrow peut tre employe pour augmenter des tableaux de tout
type, et pas seulement des tableaux dobjets :
int[] a = { 1, 2, 3, 4 };
a = (int[]) goodArrayGrow(a);

Pour permettre cela, le paramtre de goodArrayGrow est dclar de type Object, et non comme un
tableau dobjets (Object[]). Un tableau de type entier int[] peut tre converti en Object, mais un
tableau dobjets ne le peut pas !
LExemple 5.7 illustre le fonctionnement des deux mthodes daugmentation de tableau. Notez que
la conversion de type de la valeur renvoye par badArrayGrow va provoquer une exception.
Exemple 5.7 : Arraygrowtest.java
import java.lang.reflect.*;
import java.util.*;
public class ArrayGrowTest
{
public static void main(String[] args)
{
int[] a = { 1, 2, 3 };
a = (int[]) goodArrayGrow(a);
arrayPrint(a);
String[] b = { "Tom", "Dick", "Harry" };
b = (String[]) goodArrayGrow(b);
arrayPrint(b);
System.out.println
("The following call will generate an exception.");
b = (String[]) badArrayGrow(b);
}
/**
Cette mthode tente dagrandir un tableau en allouant
un nouveau tableau et en copiant tous les lments.
@param a Le tableau agrandir
@return Un tableau plus grand contenant tous les lments de a.
Toutefois, le tableau renvoy est du type Object[],
et non du mme type que a
*/
static Object[] badArrayGrow(Object[] a)
{
int newLength = a.length * 11 / 10+10;
Object[] newArray = new Object[newLength];
System.arraycopy(a, 0, newArray, 0, a.length);
return newArray;
}
/**
Cette mthode agrandit un tableau en allouant
un nouveau tableau et en copiant tous les lments.
@param a Le tableau agrandir. Peut tre un tableau object

Livre Java .book Page 235 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

235

ou un tableau du type fondamental


@return Un tableau plus grand contenant tous les lments de a.
*/
static Object goodArrayGrow(Object a)
{
Class cl = a.getClass();
if (!cl.isArray()) return null;
Class componentType = cl.getComponentType();
int length = Array.getLength(a);
int newLength = length * 11 / 10+10;
Object newArray = Array.newInstance(componentType,
newLength);
System.arraycopy(a, 0, newArray, 0, length);
return newArray;
}
/**
Une mthode permettant dafficher tous les lments dans un tableau
@param a Le tableau afficher. Peut tre un tableau object
ou un tableau du type fondamental
*/
static void arrayPrint(Object a)
{
Class cl = a.getClass();
if (!cl.isArray()) return;
Class componentType = cl.getComponentType();
int length = Array.getLength(a);
System.out.print(componentType.getName()
+"["+length+"] = { ");
for (int i = 0; i < Array.getLength(a); i++)
System.out.print(Array.get(a, i) + " ");
System.out.println("}");
}
}
java.lang.reflect.Array 1.1

static Object get(Object array, int index)


static xxx getXxx(Object array, int index)

(xxx est lun des types primitifs boolean, byte, char, double, float, int, long, short).
Ces mthodes renvoient la valeur du tableau donn, stocke lindex donn.

static void set(Object array, int index, Object newValue)


static setXxx(Object array, int index, xxx newValue)

(xxx est lun des types primitifs boolean, byte, char, double, float, int, long, short).
Ces mthodes stockent une nouvelle valeur dans le tableau donn, lindex donn.

static int getLength(Object array)

Renvoie la longueur du tableau donn.

static Object newInstance(Class componentType, int length)


static Object newInstance(Class componentType, int[] lengths)

Renvoie un nouveau tableau du type de composant donn avec les dimensions donnes.

Livre Java .book Page 236 Jeudi, 25. novembre 2004 3:04 15

236

Au cur de Java 2 - Notions fondamentales

Les pointeurs de mthodes


A premire vue, Java ne possde pas de pointeurs de mthodes ils permettent de fournir ladresse
dune mthode une autre mthode, afin que la seconde puisse appeler la premire. En fait, les
concepteurs du langage ont prcis que les pointeurs de mthodes taient dangereux et que les interfaces Java (dont nous parlerons au prochain chapitre) constituaient une meilleure solution. En
ralit, depuis le JDK 1.1, Java dispose de pointeurs de mthodes, qui sont une consquence (peuttre accidentelle) du dveloppement du package de rflexion.
INFO
Parmi les extensions non standard du langage que Microsoft a ajout son driv de Java, J++ (et son successeur, C#),
on trouve un autre type de pointeur de mthode, appel dlgu, qui est diffrent de la classe Method que nous
avons vue dans cette section. Cependant, les classes internes (que nous verrons au chapitre suivant) constituent une
construction plus utile que les dlgus.

Pour voir luvre les pointeurs de mthode, rappelez-vous que vous pouvez inspecter un champ
dun objet laide de la mthode get de la classe Field. Pour sa part, la classe Method dispose dune
mthode invoke permettant dappeler la mthode enveloppe dans lobjet Method. La signature de
la mthode invoke est la suivante :
Object invoke(Object obj, Object... args)

Le premier paramtre est implicite et les autres objets fournissent les paramtres explicites. Avant la
version JDK 5.0, vous deviez transmettre un tableau dobjets ou null si la mthode ne disposait pas
de paramtres explicites.
Dans le cas dune mthode statique, le premier paramtre est ignor il peut recevoir la valeur null.
Par exemple, si m1 reprsente la mthode getName de la classe Employee, le code suivant vous
montre comment lappeler :
String n = (String) m1.invoke(harry);

A limage des mthodes get et set du champ Field, un problme se pose si le paramtre ou le
type de rsultat est non pas une classe, mais un type primitif. Il faut soit se reposer sur lautoboxing soit, avant le JDK 5.0, envelopper tout type primitif dans sa classe enveloppe correspondante.
Inversement, si le type de retour est un type primitif, la mthode invoke renverra plutt le type enveloppe. Supposons que m2 reprsente la mthode getSalary de la classe Employee. Lobjet renvoy
est alors un objet Double, vous devez donc le transtyper en consquence. Depuis le JDK 5.0,
lunboxing automatique gre les autres oprations :
double s = (Double) m2.invoke(harry);

Comment obtenir un objet Method? Il est bien videmment possible dappeler getDeclaredMethods, qui renvoie un tableau dobjets Method dans lequel on recherchera la mthode dsire. On
peut galement appeler la mthode getMethod de la classe Class. Elle est comparable getField,
qui reoit une chane de caractres reprsentant un nom de champ et renvoie un objet Field. Cependant, puisquil peut exister plusieurs mthodes homonymes, il faut tre certain dobtenir la bonne.

Livre Java .book Page 237 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

237

Cest la raison pour laquelle il faut galement fournir les types de paramtres de la mthode dsire.
La signature de getMethod est :
Method getMethod(String name, Class... parameterTypes)

Voici, par exemple, la manire dobtenir des pointeurs sur les mthodes getName et raiseSalary de
la classe Employee:
Method m1 = Employee.class.getMethod("getName");
Method m2 = Employee.class.getMethod("raiseSalary", double.class);

Avant le JDK 5.0, vous deviez emballer les objets Class dans un tableau ou fournir null sil ny
avait pas de paramtres).
Maintenant que vous connaissez les rgles dutilisation des objets Method, nous allons les mettre en
application. Le programme de lExemple 5.8 affiche une table de valeurs pour des fonctions mathmatiques comme Math.sqrt ou Math.sin. Laffichage ressemble ceci :
public static native double java.lang.Math.sqrt(double)
1.0000 |
1.0000
2.0000 |
1.4142
3.0000 |
1.7321
4.0000 |
2.0000
5.0000 |
2.2361
6.0000 |
2.4495
7.0000 |
2.6458
8.0000 |
2.8284
9.0000 |
3.0000
10.0000 |
3.1623

Le code daffichage dune table est bien sr indpendant de la fonction relle qui saffiche sous cette
forme :
double dx = (to - from) / (n - 1);
for (double x = from; x <= to; x += dx)
{
double y = (Double) f.invoke(null,x);
System.out.printf("%10.4f | %10.4f%n" + y, x, y);
}

Ici, f est un objet de type Method. Le premier paramtre de invoke est null, car nous appelons une
mthode statique.
Pour tabuler la fonction Math.sqrt, nous dfinissons f sur :
Math.class.getMethod("sqrt", double.class)

Cest la mthode de la classe Math dont le nom est sqrt et qui a un seul paramtre de type double.
LExemple 5.8 prsente le code complet du tabulateur gnrique et quelques tests.
Exemple 5.8 : MethodPointerTest.java
import java.lang.reflect.*;
public class MethodPointerTest
{
public static void main(String[] args) throws Exception
{
/* obtenir les pointeurs sur
les mthodes square et sqrt */
Method square = MethodPointerTest.class.getMethod("square",

Livre Java .book Page 238 Jeudi, 25. novembre 2004 3:04 15

238

Au cur de Java 2 - Notions fondamentales

double.class);
Method sqrt = Math.class.getMethod("sqrt", double.class);
// afficher les tables de valeurs x- et yprintTable(1, 10, 10, square);
printTable(1, 10, 10, sqrt);
}
/**
Renvoie le carr dun nombre
@param x Un nombre
@return x au carr
*/
public static double square(double x)
{
return x * x;
}
/**
Affiche une table avec des valeurs x et y pour une mthode
@param from La limite infrieure des valeurs x
@param to La limite suprieure des valeurs x
@param n Le nombre de ranges dans la table
@param f Une mthode avec un paramtre double et
une valeur renvoye double
*/
public static void printTable(double from, double to,
int n, Method f)
{
// Affiche la mthode comme en-tte de la table
System.out.println(f);
/* construit le formateur pour affichage
avec une prcision de 4 chiffres */
double dx = (to - from) / (n - 1);
for (double x = from; x <= to; x += dx)
{
try
{
double y = (Double) f.invoke(null, x);
System.out.printf("%10.4f | %10.4f%n", x, y);
}
catch (Exception e) { e.printStackTrace(); }
}
}
}

Cet exemple est clair : on peut accomplir avec les objets Method tout ce qui est ralisable avec les
pointeurs de fonction du C (ou les dlgus du C#). A cette diffrence prs, toutefois : en langage C,
ce style de programmation est gnralement considr comme incongru et dangereux. Que se passet-il ici lorsque vous appelez une mthode en lui passant un paramtre incorrect ? La mthode invoke
dclenche une exception.

Livre Java .book Page 239 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

239

Les paramtres et le rsultat de la mthode invoke sont ncessairement de type Object. Cela signifie que lon doit effectuer un bon nombre de transtypages. En consquence, le compilateur na pas
loccasion de vrifier votre code, et les erreurs napparaissent que durant les tests, lorsquelles sont
plus difficiles corriger. De plus, une routine qui exploite la rflexion pour obtenir un pointeur de
mthode est sensiblement plus lente quun code appel qui appelle les mthodes directement.
Nous vous recommandons de nemployer les objets Method dans vos programmes quen cas dabsolue ncessit. Lutilisation des interfaces et des classes internes est de loin prfrable. Tout comme
les concepteurs de Java, nous vous conseillons de ne pas employer les objets Method pour les fonctions de rappel (callback). Lusage des interfaces pour les fonctions de rappel produit un code plus
rapide et plus facile mettre jour (nous reviendrons sur tous ces aspects au prochain chapitre).
java.lang.reflect.Method 1.1

public Object invoke(Object implicitParameter, Object[] explicitParameters)

Appelle la mthode dcrite par cet objet, en transmettant les paramtres donns et en renvoyant
la valeur que la mthode renvoie. Pour les mthodes statiques, transfrez null comme paramtre
implicite. Transfrez les valeurs des types primitifs en utilisant les enveloppes. Un type primitif
renvoie des valeurs qui ne doivent pas tre enveloppes.

Classes dnumration
Vous avez vu au Chapitre 3 comment dfinir les types numrs dans le JDK 5.0. Voici un exemple
typique :
public enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };

Le type dfini par cette dclaration est en fait une classe. La classe possde exactement quatre
instances ; il nest pas possible de construire de nouveaux objets.
Vous navez donc jamais besoin dutiliser equals pour les valeurs de types numrs. Utilisez
simplement == pour les comparer.
Vous pouvez, si vous le souhaitez, ajouter des constructeurs, des mthodes et des champs un type
numr. Bien entendu, les constructeurs ne sont invoqus que lorsque les constantes numres
sont construites. En voici un exemple :
enum Size
{
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
private Size(String abbreviation)
{ this.abbreviation = abbreviation; }
public String getAbbreviation() { return abbreviation; }
private String abbreviation;
}

Tous les types numrs sont des sous-classes de la classe Enum. Ils hritent de plusieurs mthodes
de cette classe. La mthode la plus utile est toString, qui renvoie le nom de la constante numre.
Par exemple, Size.SMALL.toString() renvoie la chane "SMALL".

Livre Java .book Page 240 Jeudi, 25. novembre 2004 3:04 15

240

Au cur de Java 2 - Notions fondamentales

Linverse de toString est la mthode statique valueOf. Par exemple, linstruction


Size s = (Size) Enum.valueOf(Size.class, "SMALL");

dfinit s sur Size.SMALL.


Chaque type numr possde une mthode de valeurs statique qui renvoie un tableau de toutes les
valeurs de lnumration :
Size[] values = Size.values();

Le petit programme de lExemple 5.9 montre comment fonctionnent les types numrs.
INFO
Tout comme Class, la classe Enum dispose dun paramtre de type que nous avons ignor pour des raisons de simplicit. Par exemple, le type numr Size tend en fait Enum<Size>.

Exemple 5.9 : EnumTest.java


import java.util.*;
public class EnumTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Enter a size:
(SMALL, MEDIUM, LARGE, EXTRA_LARGE) ");
String input = in.next().toUpperCase();
Size size = Enum.valueOf(Size.class, input);
System.out.println("size=" + size);
System.out.println("abbreviation=" + size.getAbbreviation());
if (size == Size.EXTRA_LARGE)
System.out.println("Good job--you paid attention to the _.");
}
}
enum Size
{
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
private Size(String abbreviation)
{ this.abbreviation = abbreviation; }
public String getAbbreviation() { return abbreviation; }
private String abbreviation;
}
java.lang.Enum 5.0

static Enum valueOf(Class enumClass, String name)

Renvoie la constante numre de la classe donne, avec le nom donn.

String toString()

Renvoie le nom de cette constante numre.

Livre Java .book Page 241 Jeudi, 25. novembre 2004 3:04 15

Chapitre 5

Lhritage

241

Conseils pour lutilisation de lhritage


Nous terminerons ce chapitre par quelques conseils que nous considrons utiles sur lutilisation de
lhritage.
1. Placez les oprations communes et les champs communs dans la superclasse.
Cest la raison pour laquelle nous avons plac le champ name dans la classe Person au lieu de le
dupliquer dans Employee et Student.
2. Nutilisez pas de champs protgs.
Certains programmeurs pensent que le fait de dfinir la plupart des champs dinstance comme
protected, "au cas o", est une bonne ide, afin que les sous-classes puissent accder ces
champs en cas de besoin. Pourtant, le mcanisme protected napporte pas une grande protection,
pour deux raisons. Tout dabord, le jeu des sous-classes est illimit nimporte qui peut former
une sous-classe partir de vos classes, puis crire un code permettant daccder directement aux
champs dinstance protgs, rompant ainsi lencapsulation. Et, dans Java, toutes les classes dans le
mme package ont accs aux champs protgs, quil sagisse ou non de sous-classes.
Les mthodes protected peuvent toutefois tre utiles pour indiquer celles qui ne sont pas
prvues pour un usage gnral et doivent tre redfinies dans les sous-classes. La mthode clone
en est un bon exemple.
3. Utilisez lhritage pour dterminer la relation dappartenance ("est").
Lhritage permet de rduire le code. Il est parfois employ de manire abusive. Supposons que
nous ayons besoin dune classe Contractor. Les contractants ont un nom et une date dembauche, mais ils ne peroivent pas de salaire. En revanche, ils sont pays lheure et ne restent pas
assez longtemps pour obtenir une augmentation. La tentation est grande de former une sousclasse Contractor de la classe Employee et dy ajouter un champ hourlyWage (tarif horaire).
class Contractor extends Employee
{ . . .
private double hourlyWage;
}

Ce nest pourtant pas une bonne ide, car dsormais chaque objet contractant va possder la
fois un champ salaire et un champ tarif horaire. Cela vous posera de nombreux problmes lorsque vous implmenterez des mthodes pour laffichage des fiches de paie ou des formulaires
dimpt. Finalement, vous crirez plus de code que si vous naviez pas utilis lhritage.
La relation contractant/employ ne passe pas le test dappartenance ("est"). Un contractant nest
pas un type particulier demploy.
4. Nemployez pas lhritage moins que toutes les mthodes hrites ne soient valables.
Supposons que vous dsiriez crire une classe Holiday. Chaque jour de cong est un jour et les
jours peuvent tre exprims comme des instances de la classe GregorianCalendar, nous
pouvons donc employer lhritage.
class Holiday extends GregorianCalendar { . . . }

Malheureusement, le jeu des congs nest pas ferm sous les oprations hrites. Lune des mthodes
publiques de GregorianCalendar est add. Et add peut transformer un cong en non-cong :
Holiday christmas;
christmas.add(Calendar.DAY_OF_MONTH, 12);

Livre Java .book Page 242 Jeudi, 25. novembre 2004 3:04 15

242

Au cur de Java 2 - Notions fondamentales

Lhritage nest donc pas indiqu dans ce cas.


5. Ne modifiez pas le comportement attendu lorsque vous remplacez une mthode.
Le principe de substitution sapplique non seulement la syntaxe mais surtout au comportement.
Lorsque vous remplacez une mthode, vous ne devez pas changer son comportement sans raison
valable. Le compilateur ne peut pas vous aider, il ne peut pas vrifier si vos nouvelles dfinitions
sont adaptes. Vous pouvez "rparer" le problme de la mthode add dans la classe Holiday en
redfinissant add, par exemple pour quil nait aucun rle, pour dclencher une exception ou
pour passer au prochain holiday.
Toutefois, cette rparation viole le principe de substitution. La suite dinstructions
int d1 = x.get(Calendar.DAY_OF_MONTH);
x.add(Calendar.DAY_OF_MONTH, 1);
int d2 = x.get(Calendar.DAY_OF_MONTH);
System.out.println(d2 - d1);

devrait avoir le comportement attendu, que x soit ou non du type GregorianCalendar ou Holiday.
Bien entendu, cest l quest le problme. Les gens raisonnables ou non peuvent argumenter loisir
sur le comportement attendu. Certains auteurs avancent, par exemple, que le principe de substitution
exige que Manager.equals ignore le champ de bonus car Employee.equals lignore. Ces discussions sont toujours inutiles si elles nont pas dobjectif concret. Au final, ce qui importe, cest que
vous ne dtourniez pas lintention du premier concepteur lorsque vous remplacez des mthodes dans
les sous-classes.
6. Utilisez le polymorphisme plutt que linformation de type.
Chaque fois que vous rencontrez du code ayant cette forme :
if (x est de type1)
action1(x);
else if (x est de type2)
action2(x);

pensez au polymorphisme.
Est-ce que action1 et action2 reprsentent un concept commun ? Si oui, faites-en une mthode
dune superclasse ou une interface commune aux deux types. Vous pourrez alors appeler simplement :
x.action();

et excuter laction approprie grce au mcanisme de rpartition dynamique inhrent au polymorphisme.


Le code qui emploie les interfaces ou les mthodes polymorphes est plus facile mettre jour et
amliorer que le code qui utilise des tests multiples.
7. Nabusez pas de la rflexion.
Le mcanisme de rflexion vous permet dcrire des programmes tonnamment gnralistes, en
dtectant les champs et les mthodes au moment de lexcution. Cette capacit peut tre extrmement utile pour la programmation systme, mais elle nest gnralement pas approprie pour
les applications. La rflexion est fragile le compilateur ne peut pas vous aider dtecter les
erreurs de programmation. Toutes les erreurs sont trouves au moment de lexcution et dclenchent
des exceptions.

Livre Java .book Page 243 Jeudi, 25. novembre 2004 3:04 15

6
Interfaces et classes internes
Au sommaire de ce chapitre

Interfaces
Clonage dobjets
Interfaces et callbacks
Classes internes
Proxies
Vous connaissez maintenant tous les lments de base qui concernent la programmation oriente
objet en Java. Ce chapitre prsentera plusieurs techniques avances, qui sont trs couramment utilises. Malgr leur nature peu vidente, il vous faudra les matriser pour complter votre panoplie
doutils Java.
La premire est appele interface, une faon de dcrire ce que les classes doivent faire, sans prciser
comment elles doivent le faire. Une classe peut implmenter une ou plusieurs interfaces. Vous
pouvez ensuite utiliser les objets de ces classes "implmentantes" chaque fois que la conformit avec
linterface est ncessaire. Aprs les interfaces, nous verrons le clonage (ou copie intgrale) dun
objet. Le clone dun objet est un nouvel objet qui a le mme tat que loriginal. En particulier, vous
pouvez modifier le clone sans affecter loriginal. Nous tudierons ensuite le mcanisme des classes
internes. Les classes internes sont techniquement quelque peu complexes elles sont dfinies
lintrieur dautres classes et leurs mthodes peuvent accder aux champs de la classe qui les
contient. Ces classes sont utiles pour la conception de collections de classes devant cooprer. Elles
permettent dcrire du code concis, de qualit "professionnelle", qui permet de grer les vnements
de linterface utilisateur graphique.
Ce chapitre conclura avec une discussion sur les proxies, objets qui implmentent des interfaces
arbitraires. Un proxy est une construction trs spcialise, utile pour la cration doutils systme.
Vous pouvez sauter cette section dans limmdiat.

Livre Java .book Page 244 Jeudi, 25. novembre 2004 3:04 15

244

Au cur de Java 2 - Notions fondamentales

Interfaces
Dans le langage Java, une interface nest pas une classe, mais un jeu de conditions pour les classes
qui veulent se conformer linterface.
Gnralement, le fournisseur dun service annonce : "Si votre classe se conforme une interface
particulire, je vais fournir le service". Examinons un exemple concret. La mthode sort de la classe
Arrays promet de trier un tableau dobjets, mais une condition : les objets doivent appartenir des
classes qui implmentent linterface Comparable.
Voici quoi ressemble linterface Comparable:
public interface Comparable
{
int compareTo(Object other);
}

Cela signifie que toute classe qui implmente linterface Comparable doit possder une mthode
compareTo, et la mthode doit accepter un paramtre Object et renvoyer un entier.
INFO
Depuis le JDK 5.0, linterface Comparable a t amliore pour devenir un type gnrique :
public interface Comparable<T>
{
int compareTo(T other); // le paramtre a le type T
}

Par exemple, une classe qui implmente Comparable<Employee> doit fournir une mthode :
int compareTo(Employee other)

Vous pouvez toujours utiliser le type "brut" Comparable sans paramtre de type, mais vous devrez alors transtyper
manuellement le paramtre de la mthode compareTo sur le type souhait.

Toutes les mthodes dune interface sont automatiquement public. Cest la raison pour laquelle il nest
pas ncessaire de fournir le mot cl public lorsque vous dclarez une mthode dans une interface.
Il existe une condition supplmentaire : lors de lappel x.compareTo(y), la mthode compareTo
doit en ralit tre capable de comparer deux objets et dindiquer lequel entre x ou y est le plus
grand. La mthode doit renvoyer un nombre ngatif si x est plus petit que y, zro sils sont gaux, et
un nombre positif dans les autres cas.
Cette interface particulire a une seule mthode. Certaines interfaces ont plus dune mthode.
Comme vous le verrez plus tard, les interfaces peuvent aussi dfinir des constantes. Ce qui importe,
en fait, est ce que les interfaces ne peuvent pas fournir. Les interfaces nont jamais de champs
dinstance, et les mthodes ne sont jamais implmentes dans linterface. La fourniture des
champs dinstance et des implmentations de mthode est prise en charge par les classes qui implmentent linterface. Vous pouvez imaginer une interface comme tant semblable une classe
abstraite sans champs dinstance. Il y a toutefois certaines diffrences entre ces deux concepts
nous les tudierons plus en dtail ultrieurement.

Livre Java .book Page 245 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

245

Supposons maintenant que nous voulions utiliser la mthode sort de la classe Arrays pour trier un
tableau dobjets Employee. La classe Employee doit donc implmenter linterface Comparable.
Pour quune classe implmente une interface, vous devez excuter deux tapes :
m

dclarer que votre classe a lintention dimplmenter linterface donne ;

fournir les dfinitions pour toutes les mthodes dans linterface.

Pour dclarer quune classe implmente une interface, employez le mot cl implements:
class Employee implements Comparable

Bien entendu, la classe Employee doit maintenant fournir la mthode compareTo. Supposons que
nous voulions comparer le salaire des employs. Voici une mthode compareTo qui renvoie 1 si le
salaire du premier employ est infrieur celui du second, 0 sils sont gaux, et 1 dans les autres
cas :
public int compareTo(Object otherObject)
{
Employee other = (Employee) otherObject;
if (salary < other.salary) return -1;
if (salary > other.salary) return 1;
return 0;
}

ATTENTION
Dans la dclaration de linterface, la mthode compareTo na pas t dclare public, car toutes les mthodes dans
une interface sont automatiquement publiques. Cependant, lors de limplmentation de linterface, vous devez
dclarer la mthode comme public. Sinon le compilateur suppose que la visibilit de la mthode se situe au niveau
du package ce qui est la valeur par dfaut pour une classe. Le compilateur proteste alors, car vous essayez de fournir un privilge daccs plus faible.

Avec le JDK 5.0, nous pouvons faire un peu mieux. Nous dciderons dimplmenter plutt le type
dinterface Comparable<Employee>:
class Employee implements Comparable<Employee>
{
public int compareTo(Employee other)
{
if (salary < other.salary) return -1;
if (salary > other.salary) return 1;
return 0;
}
. . .
}

Sachez que le transtypage disgracieux du paramtre Object a disparu.


ASTUCE
La mthode compareTo de linterface Comparable renvoie un entier. Si les objets ne sont pas gaux, la valeur ngative ou positive renvoye na aucune importance. Cette largesse peut tre utile si vous comparez des champs entiers.
Supposons, par exemple, que chaque employ ait un unique identificateur entier id, et que vous vouliez trier les
employs par numro dID. Vous pouvez simplement renvoyer id - other.id. Cette valeur sera ngative si le

Livre Java .book Page 246 Jeudi, 25. novembre 2004 3:04 15

246

Au cur de Java 2 - Notions fondamentales

premier ID est infrieur au second, gale 0 sils sont identiques et positive dans les autres cas. Lindication est suffisante ; la plage des entiers doit tre suffisamment petite pour quil ne se produise pas un dpassement lors de la
soustraction. Si vous savez que les ID ont une valeur non ngative ou une valeur absolue qui est au maximum
(Integer.MAX_VALUE - 1) / 2, vous ne risquez rien.
Cette astuce de soustraction nest videmment pas valable pour les nombres virgule flottante. La diffrence
salary - other.salary peut alors tre arrondie 0 si les salaires sont suffisamment proches bien que non
identiques.

Vous avez vu ce quune classe doit faire pour tirer parti du service de tri elle doit simplement
implmenter une mthode compareTo. Cest tout fait raisonnable. Il doit y avoir un moyen pour
que la mthode sort puisse comparer les objets. Mais pourquoi la classe Employee ne peut-elle tout
simplement fournir une mthode compareTo sans implmenter linterface Comparable?
Les interfaces sont ncessaires, car le langage Java est fortement typ. Lors dun appel de mthode, le
compilateur doit pouvoir vrifier que la mthode existe rellement. Quelque part dans la mthode
sort, on trouvera des instructions comme celles-ci :
if (a[i].compareTo(a[j]) > 0)
{
// rorganiser a[i] et a[j]
. . .
}

Le compilateur doit savoir que a[i] possde rellement une mthode compareTo. Si a est un tableau
dobjets Comparable, lexistence de la mthode est confirme, car chaque classe qui implmente
linterface Comparable doit fournir la mthode.
INFO
Il serait logique que la mthode sort dans la classe Arrays soit dfinie pour accepter un Tableau Comparable[],
afin que le compilateur puisse protester si quelquun appelle sort avec un tableau dont le type dlment nimplmente pas linterface Comparable. Malheureusement, il nen est rien. La mthode sort accepte un tableau
Object[] et a recours un transtypage bizarre :
// dans la bibliothque standard -- Non recommand
if (((Comparable) a[i]).compareTo(a[j]) > 0)
{
// rorganiser a[i] et a[j]
. . .
}

Si a[i] nappartient pas une classe qui implmente linterface Comparable, la machine virtuelle dclenche une
exception.

LExemple 6.1 dcrit la totalit du code ncessaire pour le tri dun tableau demploys.
Exemple 6.1 : EmployeeSortTest.java
import java.util.*;
public class EmployeeSortTest
{
public static void main(String[] args)

Livre Java .book Page 247 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

{
Employee[] staff = new Employee[3];
staff[0] = new Employee("Harry Hacker", 35000);
staff[1] = new Employee("Carl Cracker", 75000);
staff[2] = new Employee("Tony Tester", 38000);
Arrays.sort(staff);
// afficher les informations pour tous les objets Employee
for (Employee e: staff)
System.out.println("name="+e.getName()
+",salary="+e.getSalary());
}
}
class Employee implements Comparable<Employee>
{
public Employee(String n, double s)
{
name = n;
salary = s;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
/**
Compare les salaires des employs
@param other Un autre objet Employee
@return une valeur ngative si cet employ
a un salaire infrieur celui de otherObject,
0 si les salaires sont identiques,
une valeur positive dans les autres cas
*/
public int compareTo(Employee other)
{
if (salary < other.salary) return -1;
if (salary > other.salary) return 1;
return 0;
}
private String name;
private double salary;
}

247

Livre Java .book Page 248 Jeudi, 25. novembre 2004 3:04 15

248

Au cur de Java 2 - Notions fondamentales

java.lang.Comparable 1.0

int compareTo(Object otherObject)

Compare cet objet lobjet otherObject et renvoie zro sils sont identiques, un entier ngatif
sil est infrieur otherObject ou un entier positif sil est suprieur.
java.lang.Comparable<T> 5.0

int compareTo(T other)

Compare cet objet dautres et renvoie un entier ngatif si cet objet est infrieur lautre, 0 sils
sont gaux et un entier positif dans les autres cas.
java.util.Arrays 1.2

static void sort(Object[] a)

Trie les lments du tableau a, laide dun algorithme mergesort personnalis. Tous les
lments du tableau doivent appartenir des classes qui implmentent linterface Comparable;
ils doivent aussi tous tre comparables entre eux.
INFO
Selon les standards du langage : "Limplmenteur doit sassurer que sgn(x.compareTo(y)) = ?sgn(y.compareTo(x)) pour toutes les occurrences de x et y (cela implique que x.compareTo(y) doit dclencher une exception
si y.compareTo(x) dclenche une exception)." Ici, "sgn" est le signe dun nombre : sgn(n) vaut 1 si n est ngatif,
0 si n gale 0, et 1 si n est positif. En bon franais, si vous inversez les paramtres de compareTo, le signe (mais pas
ncessairement la valeur relle) du rsultat doit aussi tre invers.
Comme avec la mthode equals, des problmes peuvent survenir lorsque lon fait appel lhritage.
Etant donn que Manager tend Employee, il implmente Comparable<Employee> et non pas Comparable<Manager>. Si Manager choisit de remplacer compareTo, il doit tre prpar comparer les directeurs aux
employs. Il ne peut pas simplement transtyper lemploy en directeur :
class Manager extends Employee
{
public int compareTo(Employee other)
{
Manager otherManager = (Manager) other; // NON
. . .
}
. . .
}

La rgle d"antisymtrie" est viole. Si x est un Employee et y un Manager, lappel x.compareTo(y) ne dclenche
pas dexception il compare simplement x et y en tant quemploys. Mais linverse, y.compareTo(x) dclenche
une exception ClassCastException.
Cest la mme situation quavec la mthode equals vue au Chapitre 5 ; la solution est la mme. Il existe deux scnarios
distincts.
Si les sous-classes ont des notions diffrentes de la comparaison, vous devez interdire la comparaison des objets
appartenant des classes diffrentes. Chaque mthode compareTo devrait commencer par le test
if (getClass()!= other.getClass()) throw new ClassCastException();

Livre Java .book Page 249 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

249

Sil existe un algorithme commun pour comparer les objets de sous-classe, fournissez simplement une seule mthode
compareTo dans la superclasse et dclarez-la sous la forme final.
Supposons par exemple que vous vouliez que les directeurs aient une meilleure situation que les employs ordinaires, en dehors du salaire. Quadviendra-t-il des autres sous-classes comme Executive et Secretary? Si vous devez
tablir un ordre hirarchique, fournissez une mthode dans la classe Employee (par exemple rank). Amenez
chaque sous-classe remplacer rank et implmentez une seule mthode compareTo qui prendra en compte les
valeurs du rang.

Proprits des interfaces


Les interfaces ne sont pas des classes. En particulier, vous ne devez jamais utiliser loprateur new
pour instancier une interface :
x = new Comparable(. . .); // ERREUR

Cependant, bien que vous ne puissiez pas construire des objets interface, vous pouvez toujours
dclarer des variables interface :
Comparable x; // OK

Une variable interface doit faire rfrence un objet dune classe qui implmente linterface :
x = new Employee(. . .);
// OK du moment que Employee implmente Comparable

Ensuite, tout comme vous utilisez instanceof pour vrifier si un objet appartient une classe spcifique, vous pouvez utiliser instanceof pour vrifier si un objet implmente une interface :
if (unObjet instanceof Comparable) { . . . }

Tout comme vous pouvez construire des hirarchies de classes, vous pouvez tendre des interfaces.
Cela autorise plusieurs chanes dinterfaces allant dun plus large degr de gnralit un plus petit
degr de spcialisation. Supposez, par exemple, que vous ayez une interface appele Moveable:
public interface Moveable
{
void move(double x, double y);
}

Vous pourriez alors imaginer une interface appele Powered qui ltendrait :
public interface Powered extends Moveable
{
double milesPerGallon();
}

Bien que vous ne puissiez pas mettre des champs dinstance ou des mthodes statiques dans une
interface, vous pouvez fournir des constantes lintrieur. Par exemple :
public interface Powered extends Moveable
{
double milesPerGallon();
double SPEED_LIMIT = 95; // une constante public static final
}

Tout comme les mthodes dans une interface sont automatiquement public, les champs sont
toujours public static final.

Livre Java .book Page 250 Jeudi, 25. novembre 2004 3:04 15

250

Au cur de Java 2 - Notions fondamentales

INFO
Il est lgal de qualifier les mthodes dinterface de public, et les champs de public static final. Certains
programmeurs le font, par habitude ou pour une plus grande clart. Cependant les spcifications du langage Java
recommandent de ne pas fournir de mots cls redondants, et nous respectons cette recommandation.

Certaines interfaces dfinissent simplement les constantes et pas de mthodes. Par exemple, la
bibliothque standard contient une interface SwingConstants qui dfinit les constantes NORTH,
SOUTH, HORIZONTAL, etc. Toute classe qui choisit dimplmenter linterface SwingConstants hrite
automatiquement de ces constantes. Ses mthodes peuvent faire simplement rfrence NORTH au
lieu du SwingConstants.NORTH, plus encombrant. Toutefois, cette utilisation des interfaces semble
plutt dgnre, et nous ne la recommandons pas.
Alors que chaque classe ne peut avoir quune superclasse, les classes peuvent implmenter plusieurs
interfaces. Cela vous donne un maximum de flexibilit pour la dfinition du comportement dune
classe. Par exemple, le langage Java a une importante interface intgre, appele Cloneable (nous
reviendrons en dtail sur cette interface dans la section suivante). Si votre classe implmente Cloneable, la mthode clone dans la classe Object ralisera une copie fidle des objets de votre classe.
Supposez, par consquent, que vous vouliez disposer des fonctions de clonage et de comparaison ; il
vous suffira dimplmenter les deux interfaces :
class Employee implements Cloneable, Comparable

Utilisez des virgules pour sparer les interfaces qui dcrivent les caractristiques que vous voulez
fournir.

Interfaces et classes abstraites


Si vous avez lu la section concernant les classes abstraites au Chapitre 5, vous vous demandez peuttre pourquoi les concepteurs du langage Java se sont compliqu la tche en introduisant le concept
dinterface. Pourquoi ne pas faire de Comparable une classe abstraite ?
abstract class Comparable // Pourquoi pas?
{
public abstract int compareTo(Object other);
}

La classe Employee pourrait simplement tendre cette classe abstraite et fournir la mthode
compareTo:
class Employee extends Comparable // Pourquoi pas?
{
public int compareTo(Object other) { . . . }
}

Il y a, malheureusement, un problme majeur en ce qui concerne lutilisation dune classe de base


abstraite pour exprimer une proprit gnrique. Une classe ne peut tendre quune seule classe.
Supposons quune autre classe drive dj de la classe Employee, disons Person. La classe Employee
ne peut alors tendre une seconde classe :
class Employee extends Person, Comparable // ERREUR

En revanche, chaque classe peut implmenter autant dinterfaces quelle le souhaite :


class Employee extends Person implements Comparable // OK

Livre Java .book Page 251 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

251

Les autres langages de programmation, en particulier C++, autorisent une classe avoir plus dune
superclasse. Cette fonctionnalit est appele hritage multiple. Les concepteurs de Java ont choisi de
ne pas la prendre en charge, car elle rend le langage, soit trs complexe (comme dans C++), soit
moins efficace (comme dans Eiffel).
Les interfaces, elles, procurent la plupart des avantages de lhritage multiple tout en vitant les
risques de complexit et dinefficacit.
INFO C++
Le langage C++ prend en charge lhritage multiple, avec tous les tracas qui laccompagnent, comme les classes de
base virtuelles, les rgles de prdominance et les transtypages de pointeurs transversaux. Peu de programmeurs C++
exploitent rellement lhritage multiple, et certains affirment quil ne devrait jamais tre utilis. Dautres
conseillent de nemployer lhritage multiple que pour lhritage de style "mix-in". Dans ce style, une classe de base
primaire dcrit lobjet parent et des classes de base additionnelles (les mix-in) peuvent fournir des caractristiques
complmentaires. Ce style est comparable une classe Java ayant une seule classe de base et des interfaces additionnelles. Nanmoins, en C++, les mix-in peuvent ajouter un comportement par dfaut, ce que ne peuvent pas faire les
interfaces Java.

Clonage dobjets
Lorsque vous faites une copie dune variable, loriginal et la copie sont des rfrences au mme objet
(voir Figure 6.1). Cela signifie que tout changement apport lune des variables affecte aussi
lautre.
Employee original = new Employee("John Public", 50000);
Employee copy = original;
copy.raiseSalary(10); // ae--a aussi chang loriginal

Si vous voulez que copy soit un nouvel objet qui, au dpart, est identique original, mais dont
ltat peut diverger avec le temps, appelez la mthode clone.
Employee copy = original.clone();
copy.raiseSalary(10); // OK-original inchang

En ralit, les choses ne sont pas si simples. La mthode clone est une mthode protected de
Object, ce qui signifie que votre code ne peut pas simplement lappeler directement. Seule la
classe Employee peut cloner des objets Employee. Et il y a une bonne raison cela. Rflchissons
la manire dont la classe Object peut implmenter clone. Elle ne sait rien de lobjet et ne peut
effectuer quune copie champ champ. Si tous les champs de donnes de lobjet sont des nombres
ou appartiennent un autre type de base, la copie des champs ne posera pas de problme. Mais si
lobjet contient des rfrences des sous-objets, la copie des champs vous donnera une autre rfrence au sous-objet. En consquence, loriginal et les objets clons continueront partager certaines
informations.
Pour visualiser ce phnomne, considrons la classe Employee, prsente au Chapitre 4. La
Figure 6.2 montre ce qui se passe lorsque vous appelez la mthode clone de la classe Object pour
cloner un tel objet Employee. Vous pouvez constater que lopration de clonage par dfaut est
"superficielle" (shallow) elle ne clone pas les objets qui sont rfrencs lintrieur dautres
objets.

Livre Java .book Page 252 Jeudi, 25. novembre 2004 3:04 15

252

Au cur de Java 2 - Notions fondamentales

Le fait que cette copie soit superficielle est-il important ? Cela dpend. Si le sous-objet qui est
partag entre loriginal et le clone superficiel est inaltrable, le partage est sr. Cela se produit
certainement si le sous-objet appartient une classe inaltrable, telle que String. Le sous-objet peut
aussi tout simplement rester constant pendant toute la dure de vie de lobjet, sans que des mthodes
daltration ne laffectent, ni quaucune mthode ne produise de rfrence vers lui.
Figure 6.1

Copying

Copie et clonage.
original =

Employee

copy =

Cloning
original =

Employee

copy =

Employee

Cependant, assez frquemment, les sous-objets sont altrables, et vous devez redfinir la mthode
clone pour raliser une copie "intgrale" (deep copy) qui clone aussi les sous-objets. Dans notre
exemple, le champ hireDay est un objet Date, qui lui est altrable.
Pour chaque classe, vous devez dcider si oui ou non :
1. La mthode clone par dfaut convient.
2. La mthode clone par dfaut peut tre corrige par un appel de clone sur les sous-objets
altrables.
3. La mthode clone ne doit pas tre tente.

Livre Java .book Page 253 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Figure 6.2

Interfaces et classes internes

original =

Une copie
"shallow".

Employee

253

String

name =
salary =

50000.0

hireDay =

copy =

Employee

Date

name =
salary =

50000.0

hireDay =

La troisime option est celle par dfaut. Pour pouvoir choisir la premire ou la deuxime option, une
classe doit :
1. Implmenter linterface Cloneable.
2. Redfinir la mthode clone avec le modificateur daccs public.
INFO
La mthode clone est dclare protected dans la classe Object afin que votre code ne puisse pas simplement
appeler unObjet.clone(). Mais les mthodes protges ne sont-elles pas accessibles partir de nimporte quelle
sous-classe, et toute classe nest-elle pas une sous-classe de Object? Heureusement, les rgles concernant les accs
protgs sont plus subtiles (voir Chapitre 5). Une sous-classe peut appeler une mthode clone protge, seulement
pour cloner ses propres objets. Vous devez redfinir clone comme publique pour permettre aux objets dtre clons
par nimporte quelle mthode.

Dans ce cas, lapparence de linterface Cloneable na rien voir avec lutilisation normale des
interfaces. En particulier, elle ne spcifie pas la mthode clone qui est hrite de la classe
Object. Linterface sert purement de balise, indiquant que le concepteur de la classe comprend le
processus de clonage. Les objets sont tellement paranoaques en ce qui concerne le clonage, quils
gnrent une exception vrifie (checked exception), si un objet requiert le clonage, mais nimplmente pas cette interface.
INFO
Linterface Cloneable fait partie des quelques interfaces de balisage que fournit Java (certains programmeurs les
appellent interfaces marqueur). Souvenez-vous que lintrt habituel dune interface telle que Comparable est
dassurer quune classe implmente une mthode ou un jeu de mthodes spcifiques. Une interface de balisage na
pas de mthodes ; son seul objectif est de permettre lutilisation de instanceof dans une requte de type :
if (obj instanceof Cloneable) . . .

Nous vous recommandons de ne pas utiliser cette technique dans vos programmes.

Livre Java .book Page 254 Jeudi, 25. novembre 2004 3:04 15

254

Au cur de Java 2 - Notions fondamentales

Mme si limplmentation par dfaut (copie superficielle) de clone est adquate, vous devez toujours
implmenter linterface Cloneable, redfinir clone comme public, appeler super.clone(). Voici un
exemple :
class Employee implements Cloneable
{
// lever le niveau de visibilit public
public Employee clone() throws CloneNotSupportedException
{
return super.clone();
}
. . .
}

INFO
Avant le JDK 5.0, la mthode clone avait toujours un type de retour Object. Les types de retours covariants du JDK
5.0 vous permettent de spcifier le type de retour correct pour vos mthodes clone.

La mthode clone que vous venez de voir najoute aucune fonctionnalit la copie "shallow" fournie par Object.clone. Elle se contente de rendre la mthode public. Pour raliser une copie intgrale,
vous devrez poursuivre lexercice et cloner les champs dinstance altrables.
Voici un exemple de mthode clone qui cre une copie intgrale :
class Employee implements Cloneable
{
. . .
public Object clone() throws CloneNotSupportedException
{
// appeler Object.clone()
Employee cloned = (Employee) super.clone();
// cloner les champs altrables
cloned.hireDay = (Date) hireDay.clone()
return cloned;
}
}

La mthode clone de la classe Object menace de lancer une exception CloneNotSupportedException, et ce ds que clone est appele sur un objet dont la classe nimplmente pas linterface
Cloneable. Bien entendu, les classes Employee et Date implmentent linterface Cloneable,
lexception ne sera donc pas dclenche. Toutefois, le compilateur ne le sait pas. Nous avons donc
dclar lexception :
public Employee clone() throws CloneNotSupportedException

Vaudrait-il mieux lintercepter ?


public Employee clone()
{
try
{
return super.clone();
}

Livre Java .book Page 255 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

255

catch (CloneNotSupportedException e) { return null; }


// ceci narriverait pas, puisque nous utilisons Cloneable
}

Ceci convient bien pour les classes final. Dans les autres cas, il vaut mieux laisser le spcificateur
throws sa place. Les sous-classes ont alors loption de lancer une exception CloneNotSupportedException si elles ne prennent pas en compte le clonage.
Vous devez prendre garde lorsque vous clonez des sous-classes. Par exemple, une fois que vous avez
dfini la mthode clone pour la classe Employee, nimporte qui peut aussi cloner les objets Manager. La mthode clone Employee peut-elle assurer cette tche ? Cela dpend des champs de la classe
Manager. Dans notre cas, cela ne pose pas de problme, car le champ bonus est du type primitif.
Mais Manager pourrait avoir acquis des champs qui exigent une copie intgrale ou qui ne peuvent
pas tre clons. Il nest absolument pas garanti que limplmenteur de la sous-classe dispose dun
clone fixe pour raliser laction adquate. Vous devez donc vous assurer que la mthode clone est
dclare comme protge (protected) dans la classe Object. Mais vous naurez pas ce luxe si vous
souhaitez que les utilisateurs de vos classes puissent appeler clone.
Devez-vous alors implmenter clone dans vos propres classes ? Si vos clients doivent raliser des
copies intgrales, probablement oui. Certains auteurs considrent toutefois quil vaut mieux viter
totalement clone et implmenter sa place une autre mthode dans ce but. Nous convenons que
clone est plutt trange, mais vous rencontrerez les mmes problmes en utilisant une autre
mthode. De toute faon, le clonage est moins commun que vous pourriez le penser. Moins de 5 %
des classes de la bibliothque standard implmentent clone.
Le programme de lExemple 6.2 clone un objet Employee, puis invoque deux mthodes daltration.
La mthode raiseSalary change la valeur du champ salary, alors que la mthode setHireDay
change ltat du champ hireDay. Aucune de ces altrations naffecte lobjet original, car clone a t
dfinie pour faire une copie intgrale.
INFO
Le Chapitre 12 montre un autre mcanisme pour cloner les objets laide de la fonction de srialisation dobjet de
Java. Ce mcanisme est facile implmenter et sr, mais il nest pas trs efficace.

Exemple 6.2 : CloneTest.java


import java.util.*;
public class CloneTest
{
public static void main(String[] args)
{
try
{
Employee original = new Employee("John Q. Public", 50000);
original.setHireDay(2000, 1, 1);
Employee copy = original.clone();
copy.raiseSalary(10);
copy.setHireDay(2002, 12, 31);
System.out.println("original="+original);
System.out.println("copy="+copy);
}

Livre Java .book Page 256 Jeudi, 25. novembre 2004 3:04 15

256

Au cur de Java 2 - Notions fondamentales

catch (CloneNotSupportedException e)
{
e.printStackTrace();
}
}
}
class Employee implements Cloneable
{
public Employee(String n, double s)
{
name = n;
salary = s;
}
public Employee clone() throws CloneNotSupportedException
{
// appeler Object.clone()
Employee cloned = (Employee)super.clone();
// cloner les champs altrables
cloned.hireDay = (Date)hireDay.clone();
return cloned;
}
/**
Affecte au jour dembauche (hireday) une date donne
@param year Lanne du jour dembauche
@param month Le mois du jour dembauche
@param day Le jour dembauche
*/
public void setHireDay(int year, int month, int day)
{
hireDay = new GregorianCalendar(year, month - 1, day).getTime();
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return "Employee[name="+name
+",salary="+salary
+",hireDay="+hireDay()
+"]";
}
private String name;
private double salary;
private Date hireDay;

Livre Java .book Page 257 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

257

Interfaces et callbacks
Un pattern habituel de la programmation est celui des callbacks. Dans ce pattern, vous indiquez
laction raliser en cas de survenue dun vnement particulier. Vous pourriez vouloir, par exemple, quune action particulire survienne lorsque lon clique sur un bouton ou que lon slectionne
un lment de menu. Toutefois, comme vous navez pas encore vu comment implmenter les interfaces utilisateur, nous envisagerons une situation similaire, mais plus simple.
Le package javax.swing contient une classe Timer, utile pour tre averti de lexpiration dun dlai
imparti. Par exemple, si une partie de votre programme contient une horloge, vous pouvez demander
tre averti chaque seconde, de manire pouvoir mettre jour laffichage de lhorloge.
Lorsque vous construisez un minuteur, vous dfinissez lintervalle de temps et lui indiquez ce quil
doit faire lorsque le dlai est coul.
Comment indiquer au minuteur ce quil doit faire ? Dans de nombreux langages de programmation, vous
fournissez le nom dune fonction que le minuteur doit appeler priodiquement. Toutefois, les classes de la
bibliothque Java adoptent une approche oriente objet. Vous transfrez un objet dune classe quelconque.
Le minuteur appelle alors lune des mthodes de cet objet. Transfrer un objet est une opration plus
flexible que transfrer une fonction car lobjet peut transporter des informations complmentaires.
Bien entendu, le minuteur doit savoir quelle mthode appeler. Vous devez spcifier un objet dune classe
qui implmente linterface ActionListener du package java.awt.event. Voici cette interface :
public interface ActionListener
{
void actionPerformed(ActionEvent event);
}

Le minuteur appelle la mthode actionPerformed lorsque le dlai a expir.


INFO C++
Comme vous lavez vu au Chapitre 5, Java possde lquivalent des pointeurs de fonction, savoir les objets Method.
Ils sont toutefois difficiles utiliser, plus lents, et la scurit des types ne peut pas tre vrifie au moment de la
compilation. Ds que vous utilisez un pointeur de fonction en C++, envisagez dutiliser une interface en Java.

Supposons que vous souhaitiez afficher le message "At the tone, the time is ..." ( la tonalit, il
sera), suivi dun bip, et ce toutes les 10 secondes. Dfinissez une classe qui implmente linterface
ActionListener. Vous placerez alors toutes les instructions que vous voulez voir excuter dans la
mthode actionPerformed:
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone, the time is " + now);
Toolkit.getDefaultToolkit().beep();
}
}

Remarquez le paramtre ActionEvent de la mthode actionPerformed. Il apporte des informations sur lvnement, comme lobjet source qui la gnr (voir le Chapitre 8 pour en savoir plus).

Livre Java .book Page 258 Jeudi, 25. novembre 2004 3:04 15

258

Au cur de Java 2 - Notions fondamentales

Toutefois, il nest pas important dobtenir des informations dtailles sur ce programme, et vous
pouvez ignorer le paramtre en toute scurit.
Construisez ensuite un objet de cette classe et transmettez-le au constructeur Timer:
ActionListener listener = new TimePrinter();
Timer t = new Timer(10000, listener);

Le premier paramtre du constructeur Timer correspond au dlai qui doit scouler entre les notifications, mesur en millimes de seconde. Nous voulons tre avertis toutes les 10 secondes. Le
deuxime paramtre est lobjet couteur.
Enfin, vous dmarrez le minuteur :
t.start();

Toutes les 10 secondes, un message du type


At the tone, the time is Thu Apr 13 23:29:08 PDT 2000

saffiche, suivi dun bip.


LExemple 6.3 fait fonctionner le minuteur et son couteur. Une fois le minuteur dmarr, le
programme affiche un message et attend que lutilisateur clique sur OK pour sarrter. Entre-temps,
lheure actuelle saffiche par intervalles de 10 secondes.
Soyez patient lorsque vous excutez le programme. La bote de dialogue "Quit program?" (fermer le
programme ?) saffiche immdiatement, mais le premier message du minuteur napparat quaprs
10 secondes.
Sachez que le programme importe la classe javax.swing.Timer par nom, en plus dimporter
javax.swing.* et java.util.*. Ceci annule lambigut qui existait entre javax.swing.Timer et
java.util.Timer, une classe non lie pour programmer les tches darrire-plan.
Exemple 6.3 : TimerTest.java
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
// rsoudre le conflit avec java.util.Timer
public class TimerTest
{
public static void main(String[] args)
{
ActionListener listener = new TimePrinter();
// construit un minuteur qui appelle lcouteur
// toutes les 10 secondes
Timer t = new Timer(10000, listener);
t.start();
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
class TimePrinter implements ActionListener

Livre Java .book Page 259 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

259

{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone, the time is " + now);
Toolkit.getDefaultToolkit().beep();
}
}
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
// rsoudre le conflit avec java.util.Timer
public class TimerTest
{
public static void main(String[] args)
{
ActionListener listener = new TimePrinter();
// construit un minuteur qui appelle lcouteur
// toutes les 10 secondes
Timer t = new Timer(10000, listener);
t.start();
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone, the time is " + now);
Toolkit.getDefaultToolkit().beep();
}
}
javax.swing.JOptionPane 1.2

static void showMessageDialog(Component parent, Objectmessage)

Affiche une bote de dialogue avec une invite et un bouton OK. La bote de dialogue est centre
sur le composant parent. Si parent est null, la bote de dialogue est centre lcran.
javax.swing.Timer 1.2

Timer(int interval, ActionListener listener)

Construit un minuteur qui avertit lcouteur lorsque les millimes de seconde de lintervalle se
sont couls.

void start()

Dmarre le minuteur. Une fois lanc, il appelle actionPerformed sur ses couteurs.

void stop()

Arrte le minuteur. Une fois arrt, il nappelle plus actionPerformed sur ses couteurs.

Livre Java .book Page 260 Jeudi, 25. novembre 2004 3:04 15

260

Au cur de Java 2 - Notions fondamentales

javax.awt.Toolkit 1.0
static Toolkit getDefaultToolkit()

Rcupre la bote outils par dfaut. Une bote outils contient des informations sur lenvironnement de linterface graphique utilisateur.

void beep()

Emet un bip.

Classes internes
Une classe interne est une classe qui est dfinie lintrieur dune autre classe. Trois raisons justifient
lemploi de classes internes :
m

Les mthodes de classe internes peuvent accder aux donnes, partir de lenvergure o elles
sont dfinies, y compris les donnes qui pourraient tre des donnes prives.

Les classes internes peuvent tre caches aux autres classes du mme package.

Les classes internes anonymes sont utiles pour dfinir des callbacks sans crire beaucoup de
code.

Nous allons diviser ce sujet assez complexe en plusieurs tapes :.


m

A partir de la section suivante, vous verrez une classe interne simple qui accde un champ
dinstance de sa classe externe.

A la section "Rgles particulires de syntaxe pour les classes internes", nous verrons les rgles
de syntaxe spciales pour les classes internes.

A la section "Utilit, ncessit et scurit des classes internes", nous tudierons les classes internes pour voir comment les traduire en classes ordinaires. Les lecteurs dlicats pourront sauter
cette section.

A la section "Classes internes locales", nous discuterons des classes internes locales qui peuvent
accder aux variables locales dans la porte qui les englobe.

A la section "Classes internes anonymes", nous prsenterons les classes internes anonymes et
montrerons comment les utiliser habituellement pour implmenter les callbacks.

Enfin, partir de la section "Classes internes statiques", vous verrez comment utiliser les classes
internes statiques pour les classes daide imbriques.
INFO C++

Le langage C++ permet lemploi de classes imbriques. Une classe imbrique se situe dans la porte de la classe qui
lenglobe. Voici un exemple typique ; une classe de liste lie dfinit une classe permettant de stocker les liens et une
classe qui dtermine une position ditration :
class LinkedList
{
public:
class Iterator // une classe imbrique
{
public:
void insert(int x);
int erase();

Livre Java .book Page 261 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

261

. . .
};
. . .
private:
class Link // une classe imbrique
{
public:
Link* next;
int data;
};
. . .
};

Limbrication est une relation entre les classes, non entre les objets. Un objet LinkedList na pas de sous-objets de
types Iterator ou Link.
Cela procure deux avantages : le contrle de nom et le contrle daccs. Puisque le nom Iterator est imbriqu dans
la classe LinkedList, il est connu lextrieur sous la forme LinkedList::Iterator et ne peut pas entrer en
conflit avec une autre classe baptise Iterator. En Java, cet avantage est moins important, car les packages Java
procurent un contrle de nom quivalent. Remarquez que la classe Link se trouve dans la partie private de la
classe LinkedList. Elle est absolument invisible au reste du code. Pour cette raison, il ny a aucun risque dclarer
ses champs public. Ils ne sont accessibles quaux mthodes de la classe LinkedList (qui a le besoin lgitime dy
accder). Ils sont invisibles lextrieur de LinkedList. En Java, ce genre de contrle ntait pas possible avant
larrive des classes internes.
Les classes internes de Java possdent cependant une caractristique supplmentaire qui les rend plus riches, et donc
plus utiles que les classes imbriques de C++. Un objet dune classe interne possde une rfrence implicite lobjet
de la classe externe qui la instanci. Grce ce pointeur, il peut accder tous les champs de lobjet externe. Nous
verrons dans ce chapitre les dtails de ce mcanisme.
En Java, les classes internes static ne sont pas dotes de ce pointeur. Elles sont exactement quivalentes aux classes
imbriques de C++.

Accder ltat dun objet laide dune classe interne


La syntaxe des classes internes est assez complexe. Cest pourquoi nous utiliserons un exemple
simple, bien que peu raliste, pour dmontrer lusage des classes internes. Nous allons refactoriser
lexemple TimerTest et extraire une classe TalkingClock. Une horloge parlante se construit avec
deux paramtres : lintervalle entre les annonces et une balise pour activer ou dsactiver le bip :
class TalkingClock
{
public TalkingClock(int interval, boolean beep)
public void start() { . . . }

{ . . . }

private int interval;


private boolean beep;
private class TimePrinter implements ActionListener
// une classe interne
{
. . .
}
}

Livre Java .book Page 262 Jeudi, 25. novembre 2004 3:04 15

262

Au cur de Java 2 - Notions fondamentales

Remarquez la classe TimePrinter, qui se trouve lintrieur de la classe TalkingClock. Cela ne


signifie pas que chaque TalkingClock possde un champ dinstance TimePrinter. Comme vous le
verrez, les objets TimePrinter sont construits par les mthodes de la classe TalkingClock.
La classe TimePrinter est une classe interne prive au sein de TalkingClock. Cest un mcanisme
scuris. Seules les mthodes de TalkingClock peuvent gnrer des objets TimePrinter. Seules
les classes internes peuvent tre prives. Les classes rgulires ont toujours une visibilit publique
ou au niveau du package.
Voici la classe TimePrinter plus en dtail. Sachez que la mthode actionPerformed vrifie la
balise du bip avant dmettre le son :
private class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}

Une chose surprenante se passe. La classe TimePrinter na pas de champ dinstance ni de variable
nomme beep. En fait, beep fait rfrence au champ de lobjet TalkingClock qui a cr TimePrinter. Cest une innovation. Traditionnellement, une mthode pouvait rfrencer les champs de lobjet
qui linvoquait. Une mthode de la classe interne a accs la fois ses propres champs et ceux de
lobjet externe crateur.
Pour que tout cela fonctionne, un objet dune classe interne obtient toujours une rfrence implicite
lobjet qui la cr (voir Figure 6.3).
Figure 6.3
Un objet dune classe
interne possde une
rfrence un objet
dune classe externe.

TimePrinter
outer =

TalkingClock
interval =
beep =

1000
true

Cette rfrence est invisible dans la dfinition de la classe interne. Pour faire la lumire sur ce
concept, nous allons appeler la rfrence lobjet externe outer. La mthode actionPerformed est
alors quivalente ce qui suit :
public void actionPerformed(ActionEvent event)
{
Date now = new Date();

Livre Java .book Page 263 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

263

System.out.println("At the tone, the time is " + now);


if (outer.beep) Toolkit.getDefaultToolkit().beep();
}

La rfrence de classe externe est dfinie dans le constructeur. Etant donn que TalkingClock ne
dfinit aucun constructeur, le compilateur synthtise un constructeur, gnrant un code comme celuici :
public TimePrinter(TalkingClock clock) // Code gnr automatiquement
{
outer = clock;
}

Notez bien que outer nest pas un mot cl du langage Java. Nous lavons uniquement utilis pour
illustrer le mcanisme mis en uvre dans une classe interne.
INFO
Si une classe interne possde des constructeurs, le compilateur les modifie, en ajoutant un paramtre pour la rfrence de classe externe.

Lorsquun objet TimePrinter est construit dans la mthode start, le compilateur passe la rfrence this au constructeur dans lhorloge parlante courante :
ActionListener listener = new TimePrinter( this); //
// paramtre ajout automatiquement

LExemple 6.4 dcrit le programme complet qui teste la classe interne. Examinez de nouveau le
contrle daccs. Si la classe TimePrinter avait t une classe rgulire, il aurait fallu accder la
balise beep par lintermdiaire dune mthode publique de la classe TalkingClock. Lemploi dune
classe interne constitue une amlioration. Il nest pas ncessaire de fournir des mthodes daccs qui
nintressent quune seule autre classe.
Exemple 6.4 : InnerClassTest.java
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;
javax.swing.Timer;

public class InnerClassTest


{
public static void main(String[] args)
{
TalkingClock clock = new TalkingClock(1000, true);
clock.start();
// laisser le programme fonctionner jusqu ce que lutilisateur
// clique sur "OK"
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}

Livre Java .book Page 264 Jeudi, 25. novembre 2004 3:04 15

264

Au cur de Java 2 - Notions fondamentales

/**
Une horloge qui affiche lheure intervalles rguliers.
*/
class TalkingClock
{
/**
Construit une horloge parlante
@param interval lintervalle entre les messages (en millimes de
seconde
@param beep true si lhorloge doit sonner
*/
public TalkingClock(int interval, boolean beep)
{
this.interval = interval;
this.beep = beep;
}
/**
Lance lhorloge.
*/
public void start()
{
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
private int interval;
private boolean beep;
private class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
}

Rgles particulires de syntaxe pour les classes internes


Dans la section prcdente, nous avons explicit la rfrence de classe externe dune classe interne
en la baptisant outer. En ralit, la syntaxe correcte pour la rfrence externe est un peu plus
complexe. Lexpression
ClasseExterne.this

indique la rfrence de classe externe. Vous pouvez par exemple, crire la mthode actionPerformed
de la classe interne TimePrinter de la faon suivante :
public void actionPerformed(ActionEvent event)
{
...
if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
}

Livre Java .book Page 265 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

265

Inversement, vous pouvez crire le constructeur de lobjet interne plus explicitement, laide de la
syntaxe :
objetExterne.new ClasseInterne(paramtres de construction)

Par exemple,
ActionListener listener = this.new TimePrinter();

Ici, la rfrence la classe externe de lobjet TimePrinter nouvellement construit est dfinie par la
rfrence this de la mthode qui cre lobjet de la classe interne. Cest le cas le plus courant.
Comme toujours, lindication this. est redondante. Nanmoins, il est galement possible de donner
la rfrence de la classe externe une autre valeur en la nommant explicitement. Par exemple, si
TimePrinter est une classe interne publique, vous pouvez construire un objet TimePrinter pour
nimporte quelle horloge parlante :
TalkingClock jabberer = new TalkingClock(1000, true);
TalkingClock.TimePrinter listener = jabberer.new TimePrinter();

Notez que vous faites rfrence une classe interne de la faon suivante
ClasseExterne.ClasseInterne

lorsquelle se trouve tre hors de la porte de la classe externe. Par exemple, si TimePrinter avait
t une classe publique, vous auriez pu y faire rfrence sous la forme TalkingClock.TimePrinter
ailleurs dans votre programme.

Utilit, ncessit et scurit des classes internes


Lorsque les classes internes ont t ajoutes au langage Java dans le JDK 1.1, de nombreux programmeurs les ont considres comme une nouvelle fonctionnalit majeure qui tait hors de propos dans
la philosophie Java. La syntaxe est complexe (nous le verrons en examinant les classes internes
anonymes dans la suite de ce chapitre). Il nest pas vident de saisir linteraction des classes internes avec
dautres caractristiques du langage comme le contrle daccs et la scurit.
En ajoutant une fonctionnalit lgante et intressante plutt que ncessaire, Java a-t-il commenc
suivre la voie funeste de tant dautres langages, en se dotant dune caractristique lgante et intressante,
mais pas ncessaire ?
Nous ne donnerons pas une rponse dfinitive, mais notez que les classes internes constituent un
phnomne qui est li au compilateur et non la machine virtuelle. Les classes internes sont traduites en fichiers de classe rguliers, avec des signes $ dlimitant les noms des classes externes et internes,
et la machine virtuelle Java ne les reconnat pas comme une particularit.
Par exemple, la classe TimePrinter lintrieur de la classe TalkingClock est traduite en un
fichier de classe TalkingClock$TimePrinter.class. Pour le constater, faites cette exprience :
excutez le programme ReflectionTest du Chapitre 5 et donnez-lui comme classe de rflexion la
classe TalkingClock$TimePrinter. Vous obtiendrez ce qui suit :
class TalkingClock$TimePrinter
{
private TalkingClock$TimePrinter(TalkingClock);
TalkingClock$TimePrinter(TalkingClock, TalkingClock$1);

Livre Java .book Page 266 Jeudi, 25. novembre 2004 3:04 15

266

Au cur de Java 2 - Notions fondamentales

public void actionPerformed(java.awt.event.ActionEvent);


final TalkingClock this$0;
}

INFO
Si vous travaillez sous UNIX, noubliez pas dchapper le caractre $ si vous fournissez le nom de la classe sur la ligne
de commande. Vous devez donc excuter le programme ReflectionTest de la faon suivante :
java ReflectionTest TalkingClock$TimePrinter.

Vous pouvez voir que le compilateur a gnr un champ dinstance supplmentaire, this$0, pour la
rfrence la classe externe (le nom this$0 est synthtis par le compilateur vous ne pouvez pas
y faire rfrence dans le code source). Vous pouvez aussi voir le paramtre ajout pour le constructeur. En ralit, la squence de construction est quelque peu mystrieuse. Un constructeur priv
tablit le champ this$0 et un constructeur visible au package a un second paramtre du type TalkingClock$1, une classe visible pour le package, sans champs ni mthodes. Cette classe nest jamais
instancie. La classe TalkingClock appelle
new TalkingClock$TimePrinter(this, null)

Si le compilateur peut effectuer automatiquement cette transformation, ne pourrait-on pas simplement programmer manuellement le mme mcanisme ? Essayons. Nous allons faire de TimePrinter
une classe rgulire, extrieure la classe TalkingClock. Lors de la construction dun objet TimePrinter, nous lui passerons la rfrence this de lobjet qui le cre :
class TalkingClock
{
. . .
public void start()
{
ActionListener listener = new TimePrinter(this);
Timer t = new Timer(interval, listener);
t.start();
}
}
class TimePrinter implements ActionListener
{
public TimePrinter(TalkingClock clock)
{
outer = clock;
}
. . .
private TalkingClock outer;
}

Examinons maintenant la mthode actionPerformed. Elle doit pouvoir accder outer.beep.


if (outer.beep) . . . // ERREUR

Nous venons de rencontrer un problme. La classe interne peut accder aux donnes prives de la
classe externe, mais notre classe externe TimePrinter ne le peut pas.

Livre Java .book Page 267 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

267

Nous voyons bien que les classes internes sont intrinsquement plus puissantes que les classes rgulires, puisquelles ont un privilge daccs suprieur.
Il est lgitime de se demander comment les classes internes peuvent acqurir ce privilge daccs
suprieur, alors quelles sont traduites en classes rgulires simplement dotes de noms particuliers
la machine virtuelle ne les distingue pas. Pour rsoudre ce mystre, utilisons de nouveau le
programme ReflectionTest afin despionner la classe TalkingClock:
class TalkingClock
{
public TalkingClock(int, boolean);
static boolean access$100(TalkingClock);
public void start();
private int interval;
private boolean beep;
}

Remarquez la mthode statique access$100 ajoute par le compilateur la classe externe. Elle
renvoie le champ beep de lobjet transfr en tant que paramtre.
Elle est appele par les mthodes de la classe interne. Linstruction
if(beep)

dans la mthode actionPerformed de la classe TimePrinter ralise, en fait, lappel suivant :


if (access$100(outer));

Y a-t-il un risque pour la scurit ? Bien sr. Il est facile un tiers dinvoquer la mthode
access$100 pour lire le champ priv beep. Bien entendu, access$100 nest pas un nom autoris
pour une mthode Java. Nanmoins, pour des pirates familiers de la structure des fichiers de classe,
il est facile de produire un fichier de classe avec des instructions (machine virtuelle) qui appellent
cette mthode, par exemple en utilisant un diteur hexadcimal. Les mthodes daccs secrtes ayant
une visibilit au niveau du package, le code dattaque devrait tre plac dans le mme package que
la classe attaque.
En rsum, si une classe interne accde un champ priv, il est possible daccder ce champ par le
biais de classes ajoutes au package de la classe externe, mais une telle opration exige du talent et
de la dtermination. Un programmeur ne peut pas obtenir accidentellement un tel privilge daccs ;
pour y parvenir, il doit intentionnellement crer ou modifier un fichier de classe.

Classes internes locales


Si vous examinez attentivement le code de lexemple TalkingClock, vous constaterez que vous
navez besoin quune seule fois du nom du type TimePrinter: lorsque vous crez un objet de ce
type dans la mthode start.
Dans une telle situation, vous pouvez dfinir les classes localement lintrieur dune seule
mthode :
public void start()
{
class TimePrinter implements ActionListener
{

Livre Java .book Page 268 Jeudi, 25. novembre 2004 3:04 15

268

Au cur de Java 2 - Notions fondamentales

public void actionPerformed(ActionEvent event)


{
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(1000, listener);
t.start();
}

Les classes locales ne sont jamais dclares avec un spcificateur daccs (cest--dire public ou
private). Leur porte est toujours restreinte au bloc dans lequel elles sont dclares.
Les classes locales prsentent limmense avantage dtre compltement caches au monde extrieur,
et mme au reste du code de la classe TalkingClock. A part start, aucune mthode ne connat
lexistence de la classe TimePrinter.
Les classes locales ont un autre avantage sur les autres classes internes. Elles peuvent non seulement
accder aux champs de leurs classes externes, mais galement aux variables locales ! Ces variables
locales doivent tre dclares final. Voici un exemple typique. Dplaons les paramtres interval
et beep du constructeur TalkingClock la mthode start:
public void start(int interval, final boolean beep)
{
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(1000, listener);
t.start();
}

Notez que la classe TimePrinter na plus besoin de stocker une variable dinstance beep. Elle fait
simplement rfrence la variable paramtre de la mthode qui contient la dfinition de classe.
Cela nest peut-tre pas si surprenant. La ligne
if (beep) . . .

est situe au plus profond de la mthode start, alors pourquoi ne pourrait-elle pas avoir accs la
variable beep?
Afin de comprendre ce problme dlicat, examinons de plus prs le flux dexcution :
1. La mthode start est appele.

Livre Java .book Page 269 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

269

2. La variable objet listener est initialise par un appel au constructeur de la classe interne TimePrinter.
3. La rfrence listener est passe au constructeur de Timer, le temporisateur est dmarr, et la
mthode start se termine. A ce moment, la variable paramtre beep de la mthode start
nexiste plus.
4. Une seconde plus tard, la mthode actionPerformed sexcute if (beep) . . .:
double interest = balance * rate / 100;.

Pour que le code de la mthode actionPerformed fonctionne, la classe TimePrinter doit avoir fait
une copie du champ beep avant quil ne disparaisse en tant que variable locale de la mthode start.
Et cest exactement ce qui se passe. Dans notre exemple, le compilateur synthtise le nom TalkingClock$1TimePrinter pour la classe interne locale. Si vous utilisez le programme ReflectionTest
pour espionner la classe TalkingClock$1TimePrinter, vous obtiendrez ce qui suit :
class TalkingClock$1TimePrinter
{
TalkingClock$1TimePrinter(TalkingClock, boolean);
public void actionPerformed(java.awt.event.ActionEvent);
final boolean val$beep;
final TalkingClock this$0;
}

Remarquez le paramtre boolean supplmentaire du constructeur et la variable dinstance


val$beep. Lorsquun objet est cr, la valeur beep est passe au constructeur et stocke dans le
champ val$beep. Cela engendre bien des soucis pour les implmenteurs du compilateur. Celui-ci
doit dtecter laccs des variables locales, crer un champ de donnes pour chacune delles et copier
les variables locales dans le constructeur afin que les champs de donnes soient initialiss avec les
mmes valeurs.
Du point de vue du programmeur, toutefois, laccs aux variables locales est assez plaisant. Il simplifie vos classes internes en rduisant le nombre des champs dinstance que vous devez programmer
explicitement.
Comme nous lavons dj signal, les mthodes dune classe locale peuvent uniquement faire rfrence des variables locales dclares final. Cest la raison pour laquelle le paramtre beep a t
dclar final dans notre exemple. Une variable locale dclare final ne peut pas tre modifie
aprs avoir t initialise. Ainsi, nous avons la garantie que la variable locale et sa copie dans la
classe locale auront bien la mme valeur.
INFO
Vous avez dj vu des variables final utilises en tant que constantes, limage de ce qui suit :
public static final double SPEED_LIMIT = 55;

Le mot cl final peut tre appliqu aux variables locales, aux variables dinstance et aux variables statiques. Dans
tous les cas, cela signifie la mme chose : cette variable ne peut tre affecte quune seule fois aprs sa cration.
Il nest pas possible den modifier ultrieurement la valeur elle est dfinitive.
Cela dit, il nest pas obligatoire dinitialiser une variable final lors de sa dfinition. Par exemple, le paramtre
final beep est initialis une fois aprs sa cration, lorsque la mthode start est appele (si elle est appele

Livre Java .book Page 270 Jeudi, 25. novembre 2004 3:04 15

270

Au cur de Java 2 - Notions fondamentales

plusieurs fois, chaque appel cre son propre paramtre beep). La variable dinstance val$beep, que nous avons vue
dans la classe interne TalkingClock$1TimePrinter, est dfinie une seule fois, dans le constructeur de la classe
interne. Une variable final qui nest pas initialise lors de sa dfinition est souvent appele variable finale vide.

Classes internes anonymes


Lors de lutilisation de classes internes locales, vous pouvez souvent aller plus loin. Si vous ne dsirez crer quun seul objet de cette classe, il nest mme pas ncessaire de donner un nom la classe.
Une telle classe est appele classe interne anonyme :
public void start(int interval, final boolean beep)
{
ActionListener listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
};
Timer t = new Timer(1000, listener);
t.start();
}

Il sagit l dune syntaxe assez obscure qui signifie :


Crer un nouvel objet dune classe qui implmente linterface ActionListener, o la mthode
actionPerformed requise est celle dfinie entre les accolades { }.
Tous les paramtres employs pour construire lobjet sont donns entre les parenthses ( ) qui
suivent le nom du supertype. En gnral, la syntaxe est :
new SuperType(paramtres de construction)
{
mthodes et donnes de la classe interne
}

SuperType peut tre ici une interface telle que ActionListener; la classe interne implmente alors
cette interface. SuperType peut galement tre une classe, et dans ce cas la classe interne tend cette
classe.
Une classe interne anonyme ne peut pas avoir de constructeurs, car le nom dun constructeur doit
tre identique celui de la classe (et celle-ci na pas de nom). Au lieu de cela, les paramtres de
construction sont donns au constructeur de la superclasse. En particulier, chaque fois quune classe
interne implmente une interface, elle ne peut pas avoir de paramtres de construction. Il faut nanmoins
toujours fournir les parenthses, comme dans cet exemple :
new TypeInterface() { mthodes et donnes }

Il faut y regarder deux fois pour faire la diffrence entre la construction dun nouvel objet dune
classe rgulire et la construction dun objet dune classe interne anonyme qui tend cette classe.

Livre Java .book Page 271 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

271

Si la parenthse fermante de la liste de paramtres du constructeur est suivie dune accolade


ouvrante, cela dfinit une classe interne anonyme :
Person queen =
// un objet
Person count =
// un objet

new Person("Mary");
Person
new Person("Dracula") { ... };
dune classe interne tendant Person

Les classes internes anonymes sont-elles une brillante ide, ou plutt un excellent moyen dcrire du
code hermtique ? Sans doute les deux. Lorsque le code dune classe interne est trs court quelques lignes de code simple cela peut faire gagner un peu de temps. Mais cest exactement le genre
dconomie qui vous amne concourir pour le prix du code Java le plus obscur.
Il est dommage que les concepteurs de Java naient pas tent de perfectionner la syntaxe des classes
internes anonymes, car, le plus souvent, la syntaxe de Java constitue une amlioration par rapport
celle de C++. Les concepteurs auraient pu aider lutilisateur avec une syntaxe comme celle-ci :
Person count = new class extends Person("Dracula") { ... };
// Attention, ce nest pas la syntaxe relle de Java

Mais ils ne lont pas fait. Bien des programmeurs trouvant difficile de dchiffrer un code contenant
de trop nombreuses classes internes anonymes, il est recommand den restreindre lusage.
LExemple 6.5 contient le code source complet du programme dhorloge parlante avec une classe
interne anonyme. Si vous comparez ce programme avec celui de lExemple 6.4, vous verrez que le
code avec la classe interne anonyme est plus court, et, heureusement, avec un peu de pratique, aussi
facile comprendre.
Exemple 6.5 : AnonymousInnerClassTest.java
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;
javax.swing.Timer;

public class AnonymousInnerClassTest


{
public static void main(String[] args)
{
TalkingClock clock = new TalkingClock();
clock.start(1000, true);
// laisse le programme fonctionner jusqu ce que lutilisateur
// clique sur "OK"
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
/**
Une horloge qui affiche lheure intervalles rguliers.
*/
class TalkingClock
{
/**
Dmarre lhorloge.
@param interval lintervalle entre les messages

Livre Java .book Page 272 Jeudi, 25. novembre 2004 3:04 15

272

Au cur de Java 2 - Notions fondamentales

(en millimes de seconde)


@param beep true si lhorloge doit mettre un bip
*/
public void start(int interval, final boolean beep)
{
ActionListener listener = new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
};
Timer t = new Timer(interval, listener);
t.start();
}
}

Classes internes statiques


On dsire parfois utiliser une classe interne pour cacher simplement une classe dans une autre, sans
avoir besoin de fournir la classe interne une rfrence un objet de la classe externe. A cette fin, la
classe interne est dclare static.
Voici un exemple typique qui montre les raisons dun tel choix. Prenons le calcul des valeurs minimale et maximale dun tableau. Bien entendu, on crit normalement une mthode pour calculer
le minimum et une autre pour calculer le maximum. Lorsque ces deux mthodes sont appeles, le
tableau est parcouru deux fois. Il serait plus efficace de parcourir le tableau une seule fois et de calculer
simultanment les deux valeurs :
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (double v: values)
{
if (min > v) min = v;
if (max < v) max = v;
}

Cependant, la mthode doit renvoyer deux nombres. Pour ce faire, nous dclarons une classe Pair
qui stocke deux valeurs numriques :
class Pair
{
public Pair(double f, double s)
{
first = f;
second = s;
}
public double getFirst() { return first; }
public double getSecond() { return second; }
private double first;
private double second;
}

Livre Java .book Page 273 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

273

La fonction minmax peut alors renvoyer un objet de type Pair:


class ArrayAlg
{
public static Pair minmax(double[] values)
{
. . .
return new Pair(min, max);
}
}

Lappelant de la fonction utilise ensuite les mthodes getFirst et getSecond pour rcuprer les
rponses :
Pair p = ArrayAlg.minmax(d);
System.out.println("min = "+p.getFirst());
System.out.println("max = "+p.getSecond());

Bien sr, le nom Pair est un terme trs courant et, dans un grand projet, il se peut quun autre
programmeur ait eu la mme ide brillante, mais sa classe Pair contient une paire de chanes de
caractres. Ce conflit potentiel de noms peut tre vit en faisant de Pair une classe interne publique
dans ArrayAlg. La classe sera alors connue du public sous le nom ArrayAlg.Pair:
ArrayAlg.Pair p = ArrayAlg.minmax(d);

Quoi quil en soit, et contrairement aux autres classes internes employes dans les exemples prcdents, nous ne voulons pas avoir une rfrence un autre objet au sein dun objet Pair. Cette rfrence
peut tre supprime en dclarant la classe interne comme static:
class ArrayAlg
{
public static class Pair
{
. . .
}
. . .
}

Seules les classes internes peuvent videmment tre dclares static. Une classe interne static
ressemble exactement nimporte quelle autre classe interne, avec cette diffrence, cependant,
quun objet dune classe interne statique ne possde pas de rfrence lobjet externe qui la cr.
Dans notre exemple, nous devons utiliser une classe interne statique, car lobjet de la classe interne
est construit dans une mthode statique :
public static Pair minmax(double[] d)
{
. . .
return new Pair(min, max);
}

Si Pair navait pas t dclar static, le compilateur aurait indiqu quaucun objet implicite de
type ArrayAlg ntait disponible pour initialiser lobjet de la classe interne.
INFO
On utilise une classe interne statique lorsque la classe interne na pas besoin daccder un objet de la classe externe.
On emploie aussi le terme classe imbrique pour dsigner les classes internes statiques.

Livre Java .book Page 274 Jeudi, 25. novembre 2004 3:04 15

274

Au cur de Java 2 - Notions fondamentales

INFO
Les classes internes qui sont dclares dans une interface sont automatiquement statiques et publiques.

LExemple 6.6 contient le code source complet de la classe ArrayAlg et de la classe imbrique Pair.
Exemple 6.6 : StaticInnerClassTest.java
public class StaticInnerClassTest
{
public static void main(String[] args)
{
double[] d = new double[20];
for (int i = 0; i < d.length; i++)
d[i] = 100 * Math.random();
ArrayAlg.Pair p = ArrayAlg.minmax(d);
System.out.println("min = "+p.getFirst());
System.out.println("max = "+p.getSecond());
}
}
class ArrayAlg
{
/**
Une paire de nombres virgule flottante
*/
public static class Pair
{
/**
Construit une paire partir de
deux nombres virgule flottante
@param f Le premier nombre
@param s Le second nombre
*/
public Pair(double f, double s)
{
first = f;
second = s;
}
/**
Renvoie le premier nombre de la paire
@return le premier nombre
*/
public double getFirst()
{
return first;
}
/**
Renvoie le second nombre de la paire
@return le second nombre
*/
public double getSecond()
{
return second;
}

Livre Java .book Page 275 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

275

private double first;


private double second;
}
/**
Calcule la fois le minimum et le maximum dun tableau
@param values Un tableau de nombres virgule flottante
@return une paire dont le premier lment est le minimum et
dont le second lment est le maximum
*/
public static Pair minmax(double[] values)
{
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (double v: values)
{
if (min > v) min = v;
if (max < v) max = v;
}
return new Pair(min, max);
}
}

Proxies
Dans cette dernire section du chapitre, nous allons tudier les proxies, une fonctionnalit devenue
disponible avec la version 1.3 du JDK. Vous utilisez un proxy pour crer, au moment de lexcution,
de nouvelles classes qui implmentent un jeu donn dinterfaces. Les proxies ne sont ncessaires que
si vous ne savez pas, au moment de la compilation, quelles interfaces vous devez implmenter. Cette
situation se produit rarement pour les programmeurs dapplication. Cependant, pour certaines applications systme, la souplesse que procurent les proxies peut avoir une importance capitale. Grce
aux proxies, vous pouvez souvent viter la gnration mcanique et la compilation de code stub.
INFO
Les classes "stub" sont utilises dans plusieurs situations spcialises. Lorsque vous utilisiez linvocation de mthode
distante (RMI), un utilitaire spcial appel rmic produisait des classes stub que vous deviez ajouter votre
programme (voir le Chapitre 5 du Volume 2 pour plus dinformations au sujet de RMI). Par ailleurs, lors de lemploi
de BeanBox, des classes stub taient produites et compiles la vole lors de la connexion de beans dautres beans
(voir le Chapitre 8 du Volume 2 pour plus dinformations sur les beans Java). Depuis le JDK 5.0, la fonction de proxy
permet de gnrer les stubs RMI sans avoir excuter un utilitaire.

Supposons que vous ayez un tableau dobjets Class reprsentant des interfaces (ne contenant ventuellement quune seule interface), dont vous pouvez ne pas connatre la nature exacte au moment de
la compilation. Vous voulez alors construire un objet dune classe qui implmente ces interfaces.
Cest l un problme ardu. Si un objet Class reprsente une classe relle, vous pouvez simplement
utiliser la mthode newInstance ou la rflexion pour trouver un constructeur de cette classe. Mais
vous ne pouvez pas instancier une interface. Et vous ne pouvez pas non plus dfinir de nouvelles
classes dans un programme qui sexcute.

Livre Java .book Page 276 Jeudi, 25. novembre 2004 3:04 15

276

Au cur de Java 2 - Notions fondamentales

Pour rsoudre ce problme, certains programmes tels que la BeanBox dans les toutes premires
versions du kit de dveloppement de Bean gnraient du code, le plaaient dans un fichier, invoquaient le compilateur puis chargeaient le fichier de classe rsultant. Cette procdure est lente, bien
entendu, et demande aussi le dploiement du compilateur avec le programme. Le mcanisme de
proxy est une meilleure solution. La classe proxy peut crer des classes entirement nouvelles au
moment de lexcution. Une telle classe proxy implmente les interfaces que vous spcifiez. En
particulier, elle possde les mthodes suivantes :
m

toutes les mthodes requises par les interfaces spcifies ;

toutes les mthodes dfinies dans la classe Object (toString, equals, etc.).

Nanmoins, vous ne pouvez pas dfinir de nouveau code pour ces mthodes au moment de
lexcution. A la place, vous devez fournir un gestionnaire dinvocation. Ce gestionnaire est un
objet de toute classe implmentant linterface InvocationHandler. Cette interface possde une seule
mthode :
Object invoke(Object proxy, Method method, Object[] args)

Chaque fois quune mthode est appele sur lobjet proxy, la mthode invoke du gestionnaire
dinvocation est appele, avec lobjet Method et les paramtres de lappel original. Le gestionnaire dinvocation doit alors trouver comment grer lappel.
Pour crer un objet proxy, vous appelez la mthode newProxyInstance de la classe Proxy. Cette
mthode a trois paramtres :
m

Un chargeur de classe. Dans le cadre du modle de scurit Java, il est possible dutiliser diffrents chargeurs de classe pour les classes systme, les classes qui sont tlcharges partir
dInternet, etc. Pour linstant, nous spcifierons null pour utiliser le chargeur de classe par
dfaut.

Un tableau dobjets Class, un pour chaque interface implmenter.

Un gestionnaire dinvocation.

Deux questions restent en suspens. Comment dfinissons-nous le gestionnaire ? Et que pouvonsnous faire de lobjet proxy rsultant ? Les rponses dpendent videmment du problme que nous
voulons rsoudre avec le mcanisme de proxy. Les proxies peuvent tre employs dans bien des cas,
par exemple :
m

le routage dappels de mthode vers des serveurs distants ;

lassociation dvnements de linterface utilisateur avec des actions, dans un programme qui
sexcute ;

la trace des appels de mthode des fins de dbogage.

Dans notre exemple de programme, nous allons utiliser les proxies et les gestionnaires dinvocation pour tracer les appels de mthode. Nous dfinissons une classe enveloppe TraceHandler qui
stocke un objet envelopp (wrapped). Sa mthode invoke affiche simplement le nom et les paramtres de la mthode appeler, puis appelle la mthode avec lobjet envelopp en tant que paramtre
implicite :
class TraceHandler implements InvocationHandler
{
public TraceHandler(Object t)

Livre Java .book Page 277 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

277

{
target = t;
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
// afficher le nom et les paramtres de la mthode
. . .
// invoquer la mthode
return m.invoke(target, args);
}
private Object target;
}

Voici comment vous construisez un objet proxy qui dclenche lactivit de trace chaque fois que
lune de ses mthodes est appele :
Object value = . . .;
// construire lenveloppe (wrapper)
InvocationHandler handler = new TraceHandler(value);
// construire le proxy pour toutes les interfaces
Class[] interfaces = value.getClass().getInterfaces();
Object proxy = Proxy.newProxyInstance(null, interfaces, handler);

Maintenant, chaque fois quune mthode sera appele sur le proxy, le nom de la mthode et ses
paramtres seront affichs, puis la mthode sera invoque sur value.
Dans le programme de lExemple 6.7, les objets proxy sont employs pour raliser la trace dune
recherche binaire. Un tableau est rempli avec les valeurs entires de proxies de 1 1 000. Puis la
mthode binarySearch de la classe Arrays est invoque pour rechercher un entier alatoire dans le
tableau. Enfin, llment correspondant saffiche :
Object[] elements = new Object[1000];
// remplir les lments avec des valeurs de proxies de 1 1000
for (int i = 0; i < elements.length; i++)
{
Integer value = i+1;
elements[i] = . . .;
}
// construire un entier alatoire
Integer key = new Random().nextInt(elements.length) + 1;
// rechercher la cl (key)
int result = Arrays.binarySearch(elements, key);
// afficher la correspondance si trouve
if (result >= 0)
System.out.println(elements[result]);

La classe Integer implmente linterface Comparable. Les objets proxy appartiennent une classe
qui est dfinie au moment de lexcution (son nom est du style $Proxy0). Cette classe implmente
galement linterface Comparable. Sa mthode compareTo appelle la mthode invoke du gestionnaire de lobjet proxy.

Livre Java .book Page 278 Jeudi, 25. novembre 2004 3:04 15

278

Au cur de Java 2 - Notions fondamentales

INFO
Comme vous lavez vu plus tt dans ce chapitre, depuis le JDK 5.0 la classe Integer implmente en fait Comparable<Integer>. Toutefois, au moment de lexcution, tous les types gnriques sont effacs et le proxy est construit
avec lobjet de classe, pour la classe brute Comparable.

La mthode binarySearch ralise des appels du style :


if (elements[i].compareTo(key) < 0) . . .

Puisque nous avons complt le tableau avec des objets proxy, les appels compareTo appellent la
mthode invoke de la classe TraceHandler. Cette mthode affiche le nom de la mthode et ses
paramtres, puis invoque compareTo sur lobjet Integer envelopp.
Enfin, la fin du programme, nous appelons :
System.out.println(elements[result]);

La mthode println appelle toString sur lobjet proxy, et cet appel est aussi redirig vers le
gestionnaire dinvocation.
Voici la trace complte dune excution du programme :
500.compareTo(288)
250.compareTo(288)
375.compareTo(288)
312.compareTo(288)
281.compareTo(288)
296.compareTo(288)
288.compareTo(288)
288.toString()

Remarquez lalgorithme de recherche dichotomique qui coupe en deux lintervalle de recherche


chaque tape.
Exemple 6.7 : ProxyTest.java
import java.lang.reflect.*;
import java.util.*;
public class ProxyTest
{
public static void main(String[] args)
{
Object[] elements = new Object[1000];
// remplir les lments avec des proxies de 1 1000
for (int i = 0; i < elements.length; i++)
{
Integer value = i + 1;
Class[] interfaces = value.getClass().getInterfaces();
InvocationHandler handler = new TraceHandler(value);
Object proxy = Proxy.newProxyInstance(null,
interfaces, handler);
elements[i] = proxy;
}
// construit un entier alatoire
Integer key = new Random().nextInt(elements.length) + 1;

Livre Java .book Page 279 Jeudi, 25. novembre 2004 3:04 15

Chapitre 6

Interfaces et classes internes

279

// recherche la cl
int result = Arrays.binarySearch(elements, key);
// affiche la correspondance le cas chant
if (result >= 0) System.out.println(elements[result]);
}
}
/**
Un gestionnaire dinvocation qui affiche le nom et les paramtres
de la mthode, puis appelle la mthode initiale
*/
class TraceHandler implements InvocationHandler
{
/**
Construit un TraceHandler
@param t le paramtre implicite de lappel de mthode
*/
public TraceHandler(Object t)
{
target = t;
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
// affiche largument implicite
System.out.print(target);
// affiche le nom de la mthode
System.out.print("." + m.getName() + "(");
// affiche les arguments explicites
if (args!= null)
{
for (int i = 0; i < args.length; i++)
{
System.out.print(args[i]);
if (i < args.length - 1)
System.out.print(", ");
}
}
System.out.println(")");
// appelle la mthode relle
return m.invoke(target, args);
}
private Object target;
}

Proprits des classes proxy


Maintenant que vous avez vu les classes proxy luvre, nous allons tudier certaines de leurs
proprits. Souvenez-vous que les classes proxy sont cres la vole, dans un programme en cours
dexcution. Cependant, une fois cres, ce sont des classes rgulires, comme nimporte quelle
autre classe dans la machine virtuelle.

Livre Java .book Page 280 Jeudi, 25. novembre 2004 3:04 15

280

Au cur de Java 2 - Notions fondamentales

Toutes les classes proxy tendent la classe Proxy. Une classe proxy na quune variable dinstance
le gestionnaire dinvocation qui est dfini dans la superclasse Proxy. Toutes les donnes supplmentaires ncessaires pour excuter les tches des objets proxy doivent tre stockes dans le gestionnaire dinvocation. Par exemple, lorsque les objets Comparable ont t transforms en proxies dans
le programme de lExemple 6.7, la classe TraceHandler a envelopp les objets rels.
Toutes les classes proxy remplacent les mthodes toString, equals et hashCode de la classe
Object. Comme toutes les mthodes proxy, ces mthodes appellent simplement invoke sur le
gestionnaire dinvocation. Les autres mthodes de la classe Object (comme clone et getClass) ne
sont pas redfinies.
Les noms des classes proxy ne sont pas dfinies. La classe Proxy dans la machine virtuelle de Sun
gnre des noms de classes commenant par la chane $Proxy.
Il ny a quune classe proxy pour un chargeur de classe et un jeu ordonn dinterfaces particuliers.
Cest--dire que si vous appelez deux fois la mthode newProxyInstance avec le mme chargeur de
classe et le mme tableau dinterface, vous obtenez deux objets de la mme classe. Vous pouvez
aussi obtenir cette classe laide de la mthode getProxyClass:
Class proxyClass = Proxy.getProxyClass(null, interfaces);

Une classe proxy est toujours public et final. Si toutes les interfaces quimplmente la classe
proxy sont public, alors la classe proxy nappartient pas un package particulier. Sinon, toutes les
interfaces non publiques doivent appartenir au mme package, et la classe proxy appartient alors
aussi ce package.
Vous pouvez dterminer si un objet Class particulier reprsente une classe proxy en appelant la
mthode isProxyClass de la classe Proxy.
java.lang.reflect.InvocationHandler 1.3

Object invoke(Object proxy, Method method, Object[] args)

Dfinissez cette mthode pour quelle contienne laction que vous voulez excuter chaque fois
quune mthode a t invoque sur lobjet proxy.
java.lang.reflect.Proxy 1.3

static Class getProxyClass(ClassLoader loader, Class[] interfaces)

Renvoie la classe proxy qui implmente les interfaces donnes.

static Object newProxyInstance(ClassLoader loader, Class[] interfaces,


InvocationHandler handler)

Construit une nouvelle instance de la classe proxy qui implmente les interfaces donnes. Toutes
les mthodes appellent la mthode invoke de lobjet gestionnaire donn.

static boolean isProxyClass(Class c)

Renvoie true si c est une classe proxy.


Cela clt notre dernier chapitre sur les bases du langage de programmation Java. Les interfaces et les
classes internes sont des concepts que vous rencontrerez frquemment. Comme nous lavons
mentionn toutefois, les proxies constituent une technique pointue principalement destine aux
concepteurs doutils, et non aux programmeurs dapplication. Vous tes maintenant pars aborder
lapprentissage des techniques de programmation graphique et les interfaces utilisateur, qui
commence au Chapitre 7.

Livre Java .book Page 281 Jeudi, 25. novembre 2004 3:04 15

7
Programmation graphique
Au sommaire de ce chapitre

Introduction Swing
Cration dun cadre
Positionnement dun cadre
Affichage des informations dans un panneau
Formes 2D
Couleurs
Texte et polices
Images
Vous avez tudi jusqu prsent lcriture de programmes qui nacceptaient que des caractres
taps au clavier, les traitaient et affichaient le rsultat dans une console. De nos jours, ce nest pas
ce que dsirent la plupart des utilisateurs. Les programmes modernes et les pages Web ne fonctionnent pas de cette manire. Ce chapitre vous ouvrira la voie qui permet dcrire des programmes utilisant une interface utilisateur graphique (GUI, Graphic User Interface). Vous apprendrez
en particulier crire des programmes qui permettent de spcifier la taille et la position des fentres, dy afficher du texte avec diverses polices de caractres, de dessiner des images, et ainsi de
suite. Vous obtiendrez ainsi un ventail de comptences que nous mettrons en uvre dans les
prochains chapitres.
Les deux chapitres suivants vous montreront comment grer des vnements tels que les
frappes au clavier et les clics de la souris et comment ajouter des lments dinterfaces :
menus, boutons, etc. Aprs avoir lu ces trois chapitres, vous saurez crire des programmes
graphiques autonomes. Le Chapitre 10 abordera la programmation des applets qui emploient ces
caractristiques et qui sont intgrs dans des pages Web. Des techniques plus sophistiques de
programmation graphique sont tudies dans Au cur de Java 2 Volume 2, paru aux ditions
CampusPress, 2003.

Livre Java .book Page 282 Jeudi, 25. novembre 2004 3:04 15

282

Au cur de Java 2 - Notions fondamentales

Introduction Swing
Lors de lintroduction de Java 1.0, le programme contenait une bibliothque de classe que Sun appelait Abstract Window Toolkit (AWT) pour la programmation de base de linterface graphique utilisateur (GUI). La manire dont la bibliothque AWT de base gre les lments de linterface utilisateur
se fait par la dlgation de leur cration et de leur comportement la bote outils native du GUI sur
chaque plate-forme cible (Windows, Solaris, Macintosh, etc.). Si vous avez par exemple utilis la
version originale dAWT pour placer une bote de texte sur une fentre Java, une case de texte "pair"
sous-jacente a gr la saisie du texte. Le programme qui en rsulte pourrait alors, en thorie,
sexcuter sur lune de ces plates-formes, avec "laspect" de la plate-forme cible, do le slogan de
Sun, "Ecrire une fois, excuter partout".
Lapproche fonde sur les pairs fonctionnait bien pour les applications simples, mais il est rapidement devenu vident quil tait trs difficile dcrire une bibliothque graphique portable de haute
qualit qui dpendait des lments dune interface utilisateur native. Linterface utilisateur comme
les menus, les barres de dfilement et les champs de texte peuvent avoir des diffrences subtiles de
comportement sur diffrentes plates-formes. Il tait donc difficile doffrir aux utilisateurs une exprience cohrente et prvisible. De plus, certains environnements graphiques (comme X11/Motif) ne
disposent pas dune large collection de composants dinterface utilisateur comme lont Windows ou
Macintosh. Ceci restreint ensuite une bibliothque portable fonde sur des pairs adopter lapproche
du "plus petit dnominateur commun". En consquence, les applications GUI labores avec AWT
ntaient pas aussi jolies que les applications Windows ou Macintosh natives et navaient pas non
plus le type de fonctionnalit que les utilisateurs de ces plates-formes attendaient. Plus triste encore,
il existait diffrents bogues dans la bibliothque de linterface utilisateur AWT sur les diffrentes
plates-formes. Les dveloppeurs se sont plaints quils devaient tester leurs applications sur chaque
plate-forme, une pratique qui sest appele, avec un peu de drision, "Ecrire une fois, dboguer
partout".
En 1996, Netscape a cr une bibliothque de GUI dnomme IFC (Internet Foundation Classes) qui
utilisait une approche totalement diffrente. Les lments de linterface utilisateur, comme les
boutons, les menus, etc., taient dessins sur des fentres vierges. La seule fonctionnalit pair ncessitait une mthode pour faire apparatre les fentres et dessiner dedans. Ainsi, les lments IFC de
Netscape avaient le mme aspect et se comportaient de la mme manire, quelle que soit la plateforme dexcution du programme. Sun a collabor avec Netscape pour parfaire cette approche, avec
pour rsultat une bibliothque dinterfaces utilisateur portant le nom de code "Swing" (quelquefois
appel "Swing set").
Comme la dit Duke Ellington, "Tout a nest rien si je nai pas le swing." Et Swing est maintenant
le nom officiel du kit de dveloppement dinterface graphique lger. Swing fait partie des classes
JFC (Java Foundation Classes). Lensemble des JFC est vaste et ne se limite pas Swing. Elles
incluent non seulement les composants Swing, mais galement des API daccessibilit, de dessin 2D
et de fonctionnalits de glisser-dplacer (ou drag and drop).
INFO
Swing ne remplace pas compltement AWT, il est fond sur larchitecture AWT. Swing fournit simplement des
composants dinterface plus performants. Vous utilisez larchitecture de base dAWT, en particulier la gestion des
vnements, lorsque vous crivez un programme Swing. A partir de maintenant, nous emploierons le terme "Swing"

Livre Java .book Page 283 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

283

pour dsigner les classes allges de linterface utilisateur "dessine", et "AWT" pour dsigner les mcanismes sousjacents du kit de fentrage (tels que la gestion dvnements).
Un composant lourd est un composant utilisant les ressources du systme dexploitation hte ; un composant lger
est un composant nutilisant pas ces ressources.

Bien entendu, les lments dinterface Swing seront un peu plus lents safficher sur lcran que les
composants lourds employs par AWT. Prcisons que cette diffrence de vitesse ne pose pas de
problme sur les machines rcentes. En revanche, il existe dexcellentes raisons dopter pour
Swing :
m

Swing propose un ensemble dlments dinterface plus tendu et plus pratique.

Swing dpend peu de la plate-forme dexcution ; en consquence, il est moins sensible aux
bogues spcifiques dune plate-forme.

Swing procure une bonne exprience lutilisateur qui travaille sur plusieurs plates-formes.

Tout cela signifie que Swing remplit au moins potentiellement les conditions de la promesse
faite initialement par Sun : "Un mme programme sexcute partout".
Cependant, le troisime avantage cit reprsente galement un recul : si les lments de linterface utilisateur se ressemblent sur toutes les plates-formes, leur aspect ("look and feel") est
quand mme diffrent de celui des contrles natifs, et les utilisateurs vont devoir sadapter cette
nouvelle prsentation.
Swing rsout ce problme dune manire trs lgante. Les programmeurs dune application Swing
peuvent donner leur interface un look and feel spcifique. Les Figures 7.1 et 7.2 montrent le mme
programme en cours dexcution, lun sous Windows, lautre sous Motif (pour des raisons de copyright, le look and feel Windows nest disponible que pour les programmes Java excuts sur des
plates-formes Windows).
Figure 7.1
Application Swing
avec le look and feel
de Windows.

Livre Java .book Page 284 Jeudi, 25. novembre 2004 3:04 15

284

Au cur de Java 2 - Notions fondamentales

INFO
Bien que cela dpasse le cadre de cet ouvrage, il est possible au programmeur Java damliorer un look and feel existant ou den concevoir un qui soit entirement nouveau. Cest un travail fastidieux qui demande au programmeur
de spcifier la manire dont les divers composants Swing seront dessins. Certains dveloppeurs ont dj accompli
cette tche en portant Java sur des plates-formes non traditionnelles (telles que des terminaux de borne ou des ordinateurs de poche). Consultez le site http://javootoo.com pour obtenir une collection dimplmentation "look and
feel" intressante.
Le JDK 5.0 introduit un nouveau look and feel, appel Synth, qui facilite ce processus. Synth permet de dfinir un
nouveau look and feel en fournissant des fichiers image et des descripteurs XML, sans effectuer aucune programmation. Sun a dvelopp un look and feel indpendant de la plate-forme, baptis "Metal", jusqu ce que les spcialistes du marketing le renomment "Java look and feel". Or la plupart des programmeurs continuent utiliser le terme
"Metal", et cest ce que nous ferons dans ce livre.

Figure 7.2
Application Swing avec
le look and feel de Motif.

Certains ont fait la critique que Metal tait un peu indigeste, et son aspect a t modernis pour
la version 5.0 (voir Figure 7.3). Sachez que laspect Metal prend en charge plusieurs thmes, des
variations mineures des couleurs et des polices. Le thme par dfaut sappelle "Ocean". Dans cet
ouvrage, nous utiliserons Swing et "Metal" pour tous les programmes graphiques, avec le thme
Ocean.
INFO
La plus grande de la programmation de linterface utilisateur Java seffectue aujourdhui en Swing, une notable
exception prs. Lenvironnement de dveloppement intgr Eclipse utilise une bote outils graphique appele SWT
qui est identique AWT, faisant concorder des composants natifs sur diverses plates-formes. Vous trouverez des articles
dcrivant SWT ladresse http://www.eclipse.org/articles/.

Livre Java .book Page 285 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

285

Figure 7.3
Le look and feel "Metal"
de Swing.

Nous devons nanmoins vous donner un avertissement. Si vous avez dj programm des applications pour Windows avec Visual Basic ou C#, vous connaissez la simplicit demploi des outils
graphiques et des diteurs de ressources de ces produits. Ces logiciels permettent de concevoir
laspect dune application et gnrent une bonne partie (ou la totalit) du code de linterface. Il existe
quelques outils de dveloppement dinterface pour Java, mais ils ne sont pas aussi labors que les
outils correspondants destins Windows. Quoi quil en soit, pour comprendre parfaitement la
programmation dune interface graphique, vous devez savoir comment la construire manuellement.
Pour cela, bien entendu, il faut crire beaucoup de code.

Cration dun cadre


En Java, une fentre de haut niveau cest--dire une fentre qui nest pas contenue dans une autre
fentre est appele frame (cadre ou fentre dencadrement). Pour reprsenter ce niveau suprieur,
la bibliothque AWT possde une classe nomme Frame. La version Swing de cette classe est baptise JFrame, qui tend la classe Frame et dsigne lun des rares composants Swing qui ne soient pas
dessins sur un canevas (grille). Les lments de dcoration (boutons, barre de titre, icnes, etc.) ne
sont pas dessins par Swing, mais par le systme de fentrage de lutilisateur.
ATTENTION
La plupart des classes de composant Swing commencent par la lettre "J" : JButton, JFrame, etc. Ce sont des classes
comme Button et Frame, mais il sagit de composants AWT. Si vous omettez par inadvertance la lettre "J", votre
programme peut toujours se compiler et sexcuter, mais le mlange de Swing et de composants AWT peut amener
des incohrences visuelles et de comportement.

Livre Java .book Page 286 Jeudi, 25. novembre 2004 3:04 15

286

Au cur de Java 2 - Notions fondamentales

Les cadres sont des exemples de conteneurs. Cela signifie quils peuvent contenir dautres composants dinterface tels que des boutons et des champs de texte. Nous allons maintenant tudier les
mthodes employes le plus frquemment lorsque nous travaillons avec un composant JFrame.
LExemple 7.1 prsente un programme simple qui affiche une fentre vide sur lcran, comme le
montre la Figure 7.4.
Figure 7.4
Le plus simple
des cadres visibles.

Exemple 7.1 : SimpleFrameTest.java


import javax.swing.*;
public class SimpleFrameTest
{
public static void main(String[] args)
{
SimpleFrame frame = new SimpleFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class SimpleFrame extends JFrame
{
public SimpleFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}

Examinons ce programme ligne par ligne.


Les classes Swing se trouvent dans le package javax.swing. Le terme javax dsigne un package
dextension (pour le distinguer dun package standard). Les classes Swing constituent en fait une
extension de Java 1.1 mais, comme elles nont pas t intgres dans la hirarchie standard, il est
possible de les charger dans un navigateur compatible avec Java 1.1 (le gestionnaire de scurit du
navigateur nautorise pas lajout de packages commenant par "java."). Sur une plate-forme Java 2,
le package Swing nest plus une extension, mais fait partie de la hirarchie standard. Toute implmentation de Java qui se veut compatible avec la norme Java 2 doit fournir les classes Swing.

Livre Java .book Page 287 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

287

Quoi quil en soit, le terme javax a t conserv pour des raisons de compatibilit avec le code
Java 1.1 (en ralit, le package Swing sappelait initialement com.sun.java.swing, puis sest
brivement nomm java.awt.swing dans les premires versions bta de Java 2, avant de redevenir com.sun.java.swing dans les dernires versions bta ; aprs de nombreuses protestations
de la part des programmeurs Java, le nom javax.swing a t dfinitivement adopt).
Par dfaut, un cadre a une taille assez peu utile de 0 0 pixels. Nous dfinissons une sous-classe
SimpleFrame dont le constructeur dfinit la taille 300 200 pixels. Dans la mthode main de la
classe SimpleFrameTest, nous commenons par construire un objet SimpleFrame.
Nous indiquons ensuite ce qui doit se passer lorsque lutilisateur ferme ce cadre. Dans ce cas prcis,
le programme doit sortir. Utilisez pour cela linstruction :
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Dans dautres programmes comprenant plusieurs cadres, vous ne souhaiterez pas la sortie du
programme si lutilisateur ferme seulement lun des cadres. Par dfaut, un cadre est masqu lorsque
lutilisateur le ferme, mais le programme ne se termine pas pour autant.
Le simple fait de construire un cadre ne laffiche pas automatiquement. Les cadres sont au dpart
invisibles. Cela permet au programmeur dy ajouter des composants avant laffichage. Pour afficher
le cadre, la mthode main appelle la mthode setVisible du cadre.
La mthode main se termine ensuite. Remarquez que la sortie de main ne met pas fin lexcution
du programme, mais seulement celle du thread principal. Laffichage du cadre active un thread
dinterface utilisateur qui maintient le programme actif.
INFO
Avant le JDK 5.0, il tait possible dutiliser la mthode show, dont la classe JFrame hritait dans la superclasse
Window. Cette dernire avait elle-mme une superclasse Component qui disposait galement dune mthode show.
La mthode Component.show tait une mthode dprcie dans le JDK 1.2. Vous tes cens appeler setVisible(true) pour afficher un composant. Nanmoins, jusquau JDK 1.4, la mthode Window.show ntait pas dprcie. En fait, elle tait assez utile, pour afficher la fentre et la faire apparatre au premier plan. Malheureusement,
cet avantage a disparu avec la politique de dprciation, et le JDK 5.0 dprcie la mthode show galement pour les
fentres.

Le rsultat du programme est prsent la Figure 7.4 : il sagit dun cadre parfaitement vide et sans
intrt. La barre de titre et les lments, tels que les boutons droite, sont dessins par le systme
dexploitation et non par la bibliothque Swing. Si vous excutez le mme programme sous X
Window, les fioritures du cadre seront diffrentes. La bibliothque Swing dessine tout lintrieur
du cadre. Dans ce programme, elle emplit simplement le cadre avec une couleur darrire-plan par
dfaut.
INFO
Depuis le JDK 1.4, vous pouvez dsactiver toutes les dcorations de cadre en appelant frame.setUndecorated(true).

Livre Java .book Page 288 Jeudi, 25. novembre 2004 3:04 15

288

Au cur de Java 2 - Notions fondamentales

INFO
Dans lexemple prcdent, nous avons crit deux classes, une pour dfinir une classe cadre et une contenant une
mthode main qui cre et affiche un objet cadre. Vous verrez frquemment des programmes dans lesquels la
mthode main est place opportunment dans une classe adquate, de la faon suivante :
class SimpleFrame extends JFrame
{
public static void main(String[] args)
{
SimpleFrame frame = new SimpleFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
public SimpleFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}

Utiliser la mthode main de la classe Frame pour le code qui lance le programme est plus simple en un sens. Vous
navez pas besoin dintroduire une autre classe auxiliaire. Cependant, nombre de programmeurs trouvent ce style de
code peu clair. Pour notre part, nous prfrons sparer la classe qui lance le programme de celles qui dfinissent
linterface utilisateur.

Positionnement dun cadre


La classe JFrame ne fournit que peu de mthodes capables de modifier laspect dun cadre.
Cependant, grce lhritage, les diverses superclasses de JFrame proposent la plupart des
mthodes permettant dagir sur la taille et la position dun cadre. Les plus importantes sont les
suivantes :
m

La mthode dispose ferme la fentre et libre les ressources qui ont t employes lors de sa
cration.

La mthode setIconImage reoit un objet Image afin de lemployer comme icne lorsque la
fentre est rduite (le terme iconized est souvent utilis dans la terminologie Java).

La mthode setTitle spcifie le texte affich dans la barre de titre.

La mthode setResizable reoit un paramtre boolen pour dterminer si la taille dun cadre
peut tre modifie par lutilisateur.

La Figure 7.5 illustre la chane dhritage de la classe JFrame.


Comme lindiquent les notes API, cest gnralement dans la classe Component (anctre de tous les
objets dinterface utilisateur graphique) ou dans la classe Window (superclasse du parent de la classe
Frame) que lon recherche les mthodes permettant de modifier la taille et la position des cadres. Par
exemple, la mthode setLocation de la classe Component permet de repositionner un composant.
Si vous appelez :
setLocation(x, y)

Livre Java .book Page 289 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

289

le coin suprieur gauche du cadre est plac x pixels du bord gauche et y pixels du sommet de
lcran (0, 0) reprsente le coin suprieur gauche de lcran. De mme, la mthode setBounds
de Component permet de modifier simultanment la taille et la position dun composant (en particulier
JFrame), de la faon suivante :
setBounds(x, y, width, height)

Figure 7.5
La hirarchie dhritage
des classes JFrame et JPanel.

Object

Component

Container

JComponent

Window

JPanel

Frame

JFrame

INFO
Pour un cadre, les coordonnes de setLocation et setBounds sont relatives lcran. Pour dautres composants,
placs dans un conteneur, les cordonnes sont relatives au conteneur, comme vous le verrez au Chapitre 9.

Livre Java .book Page 290 Jeudi, 25. novembre 2004 3:04 15

290

Au cur de Java 2 - Notions fondamentales

Noubliez pas que si vous ne spcifiez pas explicitement la taille dun cadre, celui-ci aura par dfaut
une largeur et une hauteur de 0 pixel. Pour simplifier notre programme, nous avons donn au cadre
une taille qui devrait tre accepte par la plupart des systmes daffichage. Cependant, dans une
application professionnelle, vous devez dabord dterminer la rsolution de lcran afin dadapter la
taille du cadre : une fentre qui peut sembler raisonnablement grande sur lcran dun portable aura
la taille dun timbre-poste sur un cran en haute rsolution. Nous verrons bientt comment obtenir
(en pixels) les dimensions de lcran du systme. Cette information permettra ensuite de calculer la
taille optimale dun cadre.
ASTUCE
Les notes API de cette section dcrivent les mthodes les plus importantes permettant de donner le meilleur
aspect une fentre (en fonction du systme). Certaines de ces mthodes sont dfinies dans la classe JFrame.
Dautres sont hrites de diverses superclasses de JFrame. Vous devrez parfois consulter la documentation API
afin de savoir si des mthodes spcifiques sont disponibles. Malheureusement, cette recherche peut se rvler
fastidieuse avec la documentation du JDK. Pour les sous-classes, cette documentation ne dcrit que les mthodes
surcharges. Par exemple, la mthode toFront peut tre applique aux objets de type JFrame, mais elle nest
pas dcrite dans la classe JFrame, car il sagit dune mthode simplement hrite de la classe Window. Si vous
pensez quune mthode particulire devrait tre disponible et quelle ne soit pas dcrite dans la documentation
de la classe avec laquelle vous travaillez, consultez la documentation des mthodes disponibles dans les superclasses. La partie suprieure de chaque page de la documentation de lAPI contient des liens hypertexte vers les
classes anctres ; de plus, vous trouverez une liste des mthodes hrites aprs la description des nouvelles
mthodes et des mthodes surcharges.

Pour vous donner une ide de ce que lon peut faire avec une fentre, nous terminerons cette section
en vous proposant un programme de dmonstration qui positionne un des cadres et lui donne les
caractristiques suivantes :
m

Il occupe environ un quart de lcran.

Il est centr au milieu de lcran.

Par exemple, si la rsolution de lcran est de 800 600 pixels, notre cadre occupera
400 300 pixels et la position de son coin suprieur gauche sera (200, 150).
Pour connatre la taille de lcran, procdez aux tapes suivantes. Appelez la mthode statique
getDefaultToolkit de la classe Toolkit pour rcuprer lobjet Toolkit (la classe Toolkit est un
dpotoir pour diverses mthodes qui interfacent avec le systme de fentrage natif). Appelez ensuite
la mthode getScreenSize, qui renvoie la taille de lcran sous la forme dun objet Dimension (un
objet d de type Dimension stocke simultanment une largeur et une hauteur dans des variables
dinstance publiques appeles respectivement width et height).
Voici le code :
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
int screenWidth = screenSize.width;
int screenHeight = screenSize.height;

Livre Java .book Page 291 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

291

Nous fournissons galement une icne notre cadre. Comme la reprsentation des images dpend
aussi du systme, nous employons la bote outils pour charger une image. Ensuite, nous affectons
limage licne du cadre :
Image img = kit.getImage("icon.gif");
setIconImage(img);

La position de licne dpendra du systme dexploitation. Sous Windows, par exemple, licne
saffiche dans le coin suprieur gauche de la fentre, et elle apparat galement dans la liste des
tches lorsque vous appuyez sur la combinaison de touches Alt+Tab.
LExemple 7.2 prsente le programme complet. Lorsque vous excuterez le programme, remarquez
licne "Core Java".
ASTUCE
Il est assez frquent de dfinir le cadre principal dun programme sur la taille maximale. Depuis le JDK 1.4, vous
pouvez maximiser un cadre en appelant :
frame.setExtendedState(Frame.MAXIMIZED_BOTH);

INFO
Si vous crivez une application qui profite de laffichage multiple, utilisez les classes GraphicsEnvironment et
GraphicsDevice pour obtenir les dimensions des crans. Depuis le JDK 1.4, la classe GraphicsDevice vous
permet aussi dexcuter votre application en mode plein cran.

Exemple 7.2 : CenteredFrameTest.java


/**import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CenteredFrameTest
{
public static void main(String[] args)
{
CenteredFrame frame = new CenteredFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class CenteredFrame extends JFrame
{
public CenteredFrame()
{
// extraire les dimensions de lcran
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
int screenHeight = screenSize.height;
int screenWidth = screenSize.width;

Livre Java .book Page 292 Jeudi, 25. novembre 2004 3:04 15

292

Au cur de Java 2 - Notions fondamentales

// centrer le cadre au milieu de lcran


setSize(screenWidth / 2, screenHeight / 2);
setLocation(screenWidth / 4, screenHeight / 4);
// dfinir licne et le titre du cadre
Image img = kit.getImage("icon.gif");
setIconImage(img);
setTitle("CenteredFrame");
}
}
java.awt.Component 1.0

boolean isVisible()

Renvoie true si le composant est visible. Les composants sont initialement visibles par dfaut,
lexception des composants de haut niveau tels que JFrame.

void setVisible(boolean b)

Affiche ou cache le composant, selon la valeur de b (respectivement true ou false).

boolean isShowing()

Vrifie que le composant saffiche sur lcran. Pour cela, le composant doit tre visible et son
ventuel conteneur doit tre affich.

boolean isEnabled()

Vrifie que le composant est activ. Un composant activ peut recevoir le focus du clavier. Les
composants sont initialement activs.

void setEnabled(boolean b)

Active ou dsactive le composant.

Point getLocation() 1.1

Renvoie la position du coin suprieur gauche de ce composant, relativement au coin suprieur


gauche de son conteneur (un objet p de type Point encapsule des coordonnes x et y respectivement accessibles par p.x et p.y).

Point getLocationOnScreen() 1.1

Renvoie la position du coin suprieur gauche de ce composant, en coordonnes dcran.

void setBounds(int x, int y, int width, int height) 1.1

Dplace et redimensionne le composant. La position du coin suprieur gauche est spcifie par x
et y. La nouvelle taille est indique dans les paramtres width et height.

void setLocation(int x, int y) 1.1


void setLocation(Point p) 1.1

Dplace le composant. Les coordonnes x et y (ou p.x et p.y) sont relatives au conteneur si le
composant nest pas un composant de haut niveau ; elles sont relatives lcran si le composant
est de haut niveau (par exemple, un cadre JFrame).

Dimension getSize() 1.1

Renvoie la taille actuelle du composant.

Livre Java .book Page 293 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

293

void setSize(int width, int height) 1.1


void setSize(Dimension d) 1.1

Modifie la taille du composant en lui attribuant la largeur et la hauteur spcifies.


java.awt.Window 1.0

void toFront()

Affiche cette fentre par-dessus toutes les autres.

void toBack()

Place cette fentre au-dessous de la pile des fentres du bureau et rorganise les autres fentres
visibles.
java.awt.Frame 1.0

void setResizable(boolean b)

Dtermine si lutilisateur peut modifier la taille du cadre.

void setTitle(String s)

Attribue le texte de la chane s la barre de titre du cadre.

void setIconImage(Image image)

Paramtres :

image

Limage qui servira dicne ce cadre.

void setUndecorated(boolean b) 1.4

Supprime les dcorations de cadre si b vaut true.

boolean isUndecorated() 1.4

Renvoie true si ce cadre nest pas dcor.

int getExtendedState() 1.4


void setExtendedState(int state) 1.4

Rcupre ou dfinit ltat de la fentre. Ltat est lun de ceux-ci :


Frame.NORMAL
Frame.ICONIFIED
Frame.MAXIMIZED_HORIZ
Frame.MAXIMIZED_VERT
Frame.MAXIMIZED_BOTH
java.awt.Toolkit 1.0

static Toolkit getDefaultToolkit()

Renvoie la bote outils par dfaut.

Dimension getScreenSize()

Renvoie la taille de lcran.

Image getImage(String filename)

Charge une image partir du fichier spcifi par filename.

Livre Java .book Page 294 Jeudi, 25. novembre 2004 3:04 15

294

Au cur de Java 2 - Notions fondamentales

Affichage des informations dans un panneau


Nous allons voir maintenant comment afficher des informations lintrieur dun cadre. Par
exemple, au lieu dafficher "Not a Hello, World program" en mode texte dans une fentre de
console, comme nous lavons fait au Chapitre 3, nous afficherons le message dans un cadre (voir
Figure 7.6).
Figure 7.6
Un programme
graphique simple.

Il est possible de dessiner directement le message dans le cadre, mais ce nest pas considr comme
une bonne technique de programmation. En Java, les cadres sont conus pour tre les conteneurs
dautres composants (barre de menus et autres lments dinterface). On dessine normalement sur
un composant panneau pralablement ajout au cadre.
La structure de JFrame est tonnamment complexe, comme vous pouvez le constater en observant la
Figure 7.7. Vous voyez que JFrame possde quatre couches superposes. Nous navons pas nous
proccuper ici de la racine (JRoot), de la couche superpose (JLayeredPane) et de la vitre ; elles
sont ncessaires pour lorganisation de la barre de menus et du contenu, ainsi que pour implmenter
laspect (look and feel) du cadre. La partie qui intresse les programmeurs Swing est la couche
contenu. Lorsque nous concevons un cadre, nous ajoutons les composants au contenu laide
dinstructions comme celles-ci :
Container contentPane = frame.getContentPane();
Component c = . . .;
contentPane.add(c);

Jusquau JDK 1.4, la mthode add de la classe JFrame tait dfinie de manire dclencher une
exception avec le message "Do not use JFrame.add(). Use JFrame.getContentPane().add()
instead". Depuis le JDK 5.0, la mthode JFrame.add nessaie plus de rduquer les programmeurs
et appelle simplement add sur le volet de contenu.
Ainsi, depuis le JDK 5.0, vous pouvez simplement utiliser lappel :
frame.add(c);

Dans notre cas, nous dsirons uniquement ajouter au contenu un cadre sur lequel nous crirons le
message. Les panneaux sont implments par la classe JPanel. Il sagit dlments dinterface qui
offrent deux proprits particulirement utiles :
m

Ils possdent une surface sur laquelle on peut dessiner.

Ils sont eux-mmes des conteneurs.

Livre Java .book Page 295 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Figure 7.7

Programmation graphique

295

Title

La structure interne
dun cadre JFrame.

frame
root pane
layered pane
menu bar (optional)
content pane
glass pane

Ainsi, les panneaux peuvent contenir dautres composants dinterface, tels que des boutons, des
barres de dfilement, etc.
Il est prfrable davoir recours lhritage pour crer une nouvelle classe ; nous pouvons alors
surcharger ou ajouter des mthodes afin dobtenir les fonctionnalits dsires.
En particulier, pour dessiner sur un panneau, nous devons :
m

dfinir une classe qui tend JPanel;

surcharger la mthode paintComponent de cette classe.

La mthode paintComponent se trouve en fait dans JComponent la superclasse de tous les


composants Swing qui ne sont pas des fentres. Elle reoit un paramtre, de type Graphics. Un
objet Graphics mmorise une srie de paramtres pour le dessin dimages et de texte, tels que la
police dfinie ou la couleur. Un dessin en Java doit passer par un objet Graphics. Il possde des
mthodes pour dessiner des motifs, des images et du texte.
INFO
Le paramtre Graphics est comparable un contexte daffichage de Windows ou un contexte graphique en
programmation X11.

Livre Java .book Page 296 Jeudi, 25. novembre 2004 3:04 15

296

Au cur de Java 2 - Notions fondamentales

Voici comment crer un panneau, sur lequel vous pourrez dessiner :


class MyPanel extends JPanel
{
public void paintComponent(Graphics g)
{
. . . // code de dessin
}
}

Chaque fois quune fentre doit tre redessine, quelle quen soit la raison, le gestionnaire dvnement envoie une notification au composant. Les mthodes paintComponent de tous les composants
sont alors excutes.
Nappelez jamais directement la mthode paintComponent. Elle est appele automatiquement
lorsquune portion de votre application doit tre redessine ; vous ne devez pas crer dinterfrence.
Quelles sortes dactions sont dclenches par cette rponse automatique ? Par exemple, la fentre est
redessine parce que lutilisateur a modifi sa taille ou parce quil louvre nouveau aprs lavoir
rduite dans la barre des tches. De mme, si lutilisateur a ouvert, puis referm une autre fentre audessus dune fentre existante, cette dernire doit tre redessine, car son affichage a t perturb (le
systme graphique neffectue pas de sauvegarde des pixels cachs). Bien entendu, lors de sa
premire ouverture, une fentre doit excuter le code qui indique comment, et o, les lments
quelle contient sont affichs.
ASTUCE
Si vous devez forcer une fentre se redessiner, appelez la mthode repaint plutt que paintComponent. La
mthode repaint appelle paintComponent pour tous les composants de la fentre, en fournissant chaque fois un
objet Graphics appropri.

Comme vous avez pu le constater dans le fragment de code prcdent, la mthode paintComponent
prend un seul paramtre de type Graphics. Les coordonnes et les dimensions appliques cet objet
sont exprimes en pixels. Les coordonnes (0, 0) reprsentent le coin suprieur gauche du composant
sur lequel vous dessinez.
Laffichage de texte est considr comme une forme particulire de dessin. La classe Graphics
dispose dune mthode drawString dont voici la syntaxe :
g.drawString(text, x, y)

Nous souhaitons dessiner la chane "Not a Hello, World program" peu prs au centre de la
fentre originale. Bien que nous ne sachions pas encore comment mesurer la taille de la chane, nous
la ferons commencer la position (75, 100). Cela signifie que le premier caractre sera situ
75 pixels du bord gauche et 100 pixels du bord suprieur de la fentre (en fait, cest la ligne de base
du texte qui se situe 100 pixels nous verrons bientt comment mesurer le texte). Notre mthode
paintComponent ressemble donc ceci :
class NotHelloWorldPanel extends JPanel
{
public void paintComponent(Graphics g)
{
. . . // voir ci-aprs

Livre Java .book Page 297 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

297

g.drawString("Not a Hello, World program",


MESSAGE_X, MESSAGE_Y);
}
public static final int MESSAGE_X = 75;
public static final int MESSAGE_Y = 100;
}

Mais notre mthode paintComponent nest pas complte. La classe NotHelloWorldPanel drive de
la classe JPanel, qui a sa propre ide sur la manire de dessiner le panneau en le remplissant avec
la couleur de fond. Pour sassurer que la superclasse effectue sa part du travail, nous devons appeler
super.paintComponent avant de procder notre propre opration de dessin :
class NotHelloWorldPanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
. . . // code de dessin
}
}

LExemple 7.3 prsente le code complet du programme. Si vous utilisez le JDK 1.4 ou version antrieure, noubliez pas de transformer lappel add(panel) par getContentPane().add(panel).
Exemple 7.3 : NotHelloWorld.java
import javax.swing.*;
import java.awt.*;
public class NotHelloWorld
{
public static void main(String[] args)
{
NotHelloWorldFrame frame = new NotHelloWorldFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre qui contient un panneau de message
*/
class NotHelloWorldFrame extends JFrame
{
public NotHelloWorldFrame()
{
setTitle("NotHelloWorld");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter le panneau au cadre
NotHelloWorldPanel panel = new NotHelloWorldPanel();
add(panel);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}

Livre Java .book Page 298 Jeudi, 25. novembre 2004 3:04 15

298

Au cur de Java 2 - Notions fondamentales

/**
Un panneau qui affiche un message.
*/
class NotHelloWorldPanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawString("Not a Hello, World program",
MESSAGE_X, MESSAGE_Y);
}
public static final int MESSAGE_X = 75;
public static final int MESSAGE_Y = 100;
}
javax.swing.JFrame 1.2

Container getContentPane()

Renvoie lobjet du panneau de contenu pour JFrame.

void add(Component c)

Ajoute le composant donn au volet de ce cadre (avant le JDK 5.0, cette mthode dclenchait
une exception).
java.awt.Component 1.0

void repaint()

Provoque un redessin du composant "ds que possible".

public void repaint(int x, int y, int width, int height)

Provoque un redessin dune partie du composant "ds que possible".


javax.swing.JComponent 1.2

void paintComponent(Graphics g)

Surchargez cette mthode pour dcrire la manire dont votre composant doit tre dessin.

Formes 2D
Depuis la version 1.0 de JDK, la classe Graphics disposait de mthodes pour dessiner des lignes,
des rectangles, des ellipses, etc. Mais ces oprations de dessin sont trs limites. Par exemple, vous
ne pouvez pas tracer des traits dpaisseurs diffrentes ni faire pivoter les formes.
Le JDK 1.2 a introduit la bibliothque Java 2D qui implmente un jeu doprations graphiques trs
puissantes. Dans ce chapitre, nous allons seulement examiner les fonctions de base de la bibliothque Java 2D. Consultez le chapitre du Volume 2 traitant des fonctions AWT avances pour plus
dinformations sur les techniques sophistiques.
Pour dessiner des formes dans la bibliothque Java 2D, vous devez obtenir un objet de la classe
Graphics2D. Il sagit dune sous-classe de la classe Graphics. Si votre version du JDK est

Livre Java .book Page 299 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

299

compatible Java 2D, les mthodes telles que paintComponent reoivent automatiquement un
objet de la classe Graphics2D. Il suffit davoir recours un transtypage, de la faon suivante :
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
. . .
}

La bibliothque Java 2D organise les formes gomtriques dune faon oriente objet. En particulier,
il existe des classes pour reprsenter des lignes, des rectangles et des ellipses :
Line2D
Rectangle2D
Ellipse2D

Ces classes implmentent toutes linterface Shape.


INFO
La bibliothque Java 2D gre des formes plus complexes en particulier, les arcs, les courbes quadratiques et cubiques
et les objets "general path". Voir le Chapitre 7 du Volume 2 pour plus dinformations.

Pour dessiner une forme, vous devez dabord crer un objet dune classe qui implmente linterface
Shape puis appeler la mthode draw de la classe Graphics2D. Par exemple :
Rectangle2D rect = . . .;
g2.draw(rect);

INFO
Avant lapparition de la bibliothque Java 2D, les programmeurs utilisaient les mthodes de la classe Graphics
telles que drawRectangle pour dessiner des formes. En apparence, les appels de mthode de lancien style paraissent plus simples. Cependant, avec la bibliothque Java 2D, vos options restent ouvertes vous pouvez ultrieurement
amliorer vos dessins laide des nombreux outils que fournit la bibliothque.

Lutilisation des classes de formes Java 2D amne une certaine complexit. Contrairement aux
mthodes de la version 1.0, qui utilisaient des entiers pour les coordonnes de pixels, Java 2D
emploie des valeurs de coordonnes en virgule flottante. Cela est souvent pratique, car vous pouvez
spcifier pour vos formes des coordonnes qui sont significatives pour vous (comme des millimtres
par exemple), puis les traduire en pixels. La bibliothque Java 2D utilise des valeurs float simple
prcision pour nombre de ses calculs internes en virgule flottante. La simple prcision est suffisante
aprs tout, le but ultime des calculs gomtriques est de dfinir des pixels lcran ou sur limprimante. Tant quune erreur darrondi reste cantonne un pixel, laspect visuel nest pas affect. De
plus, les calculs en virgule flottante sont plus rapides sur certaines plates-formes et les valeurs float
requirent un volume de stockage rduit de moiti par rapport aux valeurs double.
Cependant, la manipulation de valeurs float est parfois peu pratique pour le programmeur, car le
langage Java est inflexible en ce qui concerne les transtypages ncessaires pour la conversion de
valeurs double en valeurs float. Par exemple, examinez linstruction suivante :
float f = 1.2; // Erreur

Livre Java .book Page 300 Jeudi, 25. novembre 2004 3:04 15

300

Au cur de Java 2 - Notions fondamentales

Cette instruction choue la compilation, car la constante 1.2 est du type double, et le compilateur
est trs susceptible en ce qui concerne la perte de prcision. Le remde consiste ajouter un suffixe
F la constante virgule flottante :
float f = 1.2F; // Ok

Considrez maintenant linstruction


Rectangle2D r = . . .
float f = r.getWidth(); // Erreur

Cette instruction chouera galement la compilation, pour la mme raison. La mthode getWidth
renvoie un double. Cette fois, le remde consiste prvoir un transtypage :
float f = (float)r.getWidth(); // Ok

Les suffixes et les transtypages tant un inconvnient vident, les concepteurs de la bibliothque 2D
ont dcid de fournir deux versions de chaque classe de forme : une avec des coordonnes float
pour les programmeurs conomes, et une avec des coordonnes double pour les paresseux (dans cet
ouvrage, nous nous rangerons du ct des seconds, et nous utiliserons des coordonnes double dans
la mesure du possible).
Les concepteurs de la bibliothque ont choisi une mthode curieuse, et assez droutante au premier
abord, pour le packaging de ces choix. Examinez la classe Rectangle2D. Cest une classe abstraite,
avec deux sous-classes concrtes, qui sont aussi des classes internes statiques :
Rectangle2D.Float
Rectangle2D.Double

La Figure 7.8 montre le schma dhritage.


Figure 7.8
Les classes rectangle 2D.

Rectangle2D

Rectangle2D
.Float

Rectangle2D
.Double

Il est prfrable de tenter dignorer le fait que les deux classes concrtes sont internes statiques
cest juste une astuce pour viter davoir fournir des noms tels que FloatRectangle2D et
DoubleRectangle2D (pour plus dinformations au sujet des classes internes statiques, reportez-vous
au Chapitre 6).
Lorsque vous construisez un objet Rectangle2D.Float, vous fournissez les coordonnes en tant
que nombres float. Pour un objet Rectangle2D.Double, vous fournissez des nombres de type
double:
Rectangle2D.Float floatRect = new Rectangle2D.Float(10.0F,
25.0F, 22.5F, 20.0F);

Livre Java .book Page 301 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

301

Rectangle2D.Double doubleRect = new Rectangle2D.Double(10.0,


25.0, 22.5, 20.0);

En ralit, puisque la fois Rectangle2D.Float et Rectangle2D.Double tendent la classe


commune Rectangle2D, et que les mthodes dans les sous-classes surchargent simplement les
mthodes dans la superclasse Rectangle2D, il ny a aucun intrt mmoriser le type exact de
forme. Vous pouvez simplement employer des variables Rectangle2D pour stocker les rfrences
du rectangle :
Rectangle2D floatRect = new Rectangle2D.Float(10.0F,
25.0F, 22.5F, 20.0F);
Rectangle2D doubleRect = new Rectangle2D.Double(10.0,
25.0, 22.5, 20.0);

Cest--dire que vous navez besoin dutiliser les empoisonnantes classes internes que lorsque vous
construisez les objets formes.
Les paramtres de construction indiquent le coin suprieur gauche, la largeur et la hauteur du
rectangle.
INFO
En ralit, la classe Rectangle2D.Float possde une mthode supplmentaire qui nest pas hrite de
Rectangle2D, il sagit de setRect(float x, float y, float h, float w). Vous perdez cette mthode si
vous stockez la rfrence Rectangle2D.Float dans une variable Rectangle2D. Mais ce nest pas une grosse perte
la classe Rectangle2D dispose dune mthode setRect avec des paramtres double.

Les mthodes de Rectangle2D utilisent des paramtres et des valeurs renvoyes de type double. Par
exemple, la mthode getWidth renvoie une valeur double, mme si la largeur est stocke sous la
forme de float dans un objet Rectangle2D.Float.
ASTUCE
Utilisez simplement les classes de forme Double pour viter davoir manipuler des valeurs float. Cependant, si
vous construisez des milliers dobjets forme, envisagez demployer les classes Float pour conomiser la mmoire.

Ce que nous venons de voir pour les classes Rectangle2D est valable aussi pour les autres classes.
Il existe de plus une classe Point2D avec les sous-classes Point2D.Float et Point2D.Double.
Voici comment lutiliser :
Point2D p = new Point2D.Double(10, 20);

ASTUCE
La classe Point2D est trs utile elle est plus oriente objet pour fonctionner avec les objets Point2D quavec des
valeurs x et y spares. De nombreux constructeurs et mthodes acceptent des paramtres Point2D. Nous vous
suggrons dutiliser des objets Point2D autant que possible ils facilitent gnralement la comprhension des
calculs gomtriques.

Livre Java .book Page 302 Jeudi, 25. novembre 2004 3:04 15

302

Au cur de Java 2 - Notions fondamentales

Les classes Rectangle2D et Ellipse2D hritent toutes deux dune superclasse commune RectangularShape. Bien sr, les ellipses ne sont pas rectangulaires, mais elles sont incluses dans un
rectangle englobant (voir Figure 7.9).
Figure 7.9
Le rectangle englobant
dune ellipse.

La classe RectangularShape dfinit plus de 20 mthodes qui sont communes ces formes, parmi
lesquelles les incontournables getWidth, getHeight, getCenterX et getCenterY (mais malheureusement, au moment o nous crivons ces lignes, il nexiste pas de mthode getCenter qui renverrait
le centre sous la forme dun objet Point2D).
On trouve enfin deux classes hrites de JDK 1.0 qui ont t insres dans la hirarchie de la classe
Shape. Les classes Rectangle et Point, qui stockent un rectangle et un point avec des coordonnes
entires, tendent les classes Rectangle2D et Point2D.
La Figure 7.10 montre les relations entre les classes Shape. Les sous-classes Double et Float sont
omises. Les classes hrites sont grises.
Les objets Rectangle2D et Ellipse2D sont simples construire. Vous devez spcifier :
m

les coordonnes x et y du coin suprieur gauche ;

la largeur et la hauteur.

Figure 7.10
Relations entre
les classes Shape.

Shape

Point2D

Point

Rectangular
Shape

Line2D

Ellipse2D

Rectangle2D

Rectangle

Livre Java .book Page 303 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

303

Pour les ellipses, ces valeurs font rfrence au rectangle englobant.


Par exemple,
Ellipse2D e = new Ellipse2D.Double(150, 200, 100, 50);

construit une ellipse englobe dans un rectangle dont le coin suprieur gauche a les coordonnes
(150, 200), avec une largeur de 100, et une hauteur de 50.
Il arrive que vous ne disposiez pas directement des valeurs du coin suprieur gauche. Il est assez
frquent davoir les deux coins opposs de la diagonale dun rectangle, mais peut-tre quil ne sagit
pas des coins suprieur gauche et infrieur droit. Vous ne pouvez pas construire simplement un
rectangle ainsi :
Rectangle2D rect = new Rectangle2D.Double(px, py,
qx - px, qy - py); // Erreur

Si p nest pas le coin suprieur gauche, lune des diffrences de coordonnes, ou les deux, seront
ngatives et le rectangle sera vide. Dans ce cas, crez dabord un rectangle vide et utilisez la
mthode setFrameFromDiagonal:
Rectangle2D rect = new Rectangle2D.Double();
rect.setFrameFromDiagonal(px, py, qx, qy);

Ou, mieux encore, si vous connaissez les points dangle en tant quobjets Point2D, p et q:
rect.setFrameFromDiagonal(p, q);

Lors de la construction dune ellipse, vous connaissez gnralement le centre, la largeur et la


hauteur, mais pas les points des angles du rectangle englobant (qui ne reposent pas sur lellipse).
Il existe une mthode setFrameFromCenter qui utilise le point central, mais qui requiert
toujours lun des quatre points dangle. Vous construisez donc gnralement une ellipse de la
faon suivante :
Ellipse2D ellipse = new Ellipse2D.Double(centerX - width / 2,
centerY - height / 2, width, height);

Pour construire une ligne, vous fournissez les points de dpart et darrive, sous la forme dobjets
Point2D ou de paires de nombres :
Line2D line = new Line2D.Double(start, end);

ou
Line2D line = new Line2D.Double(startX, startY, endX, endY);

Le programme de lExemple 7.4 trace :


m

un rectangle ;

lellipse incluse dans le rectangle ;

une diagonale du rectangle ;

un cercle ayant le mme centre que le rectangle.

La Figure 7.11 montre le rsultat.

Livre Java .book Page 304 Jeudi, 25. novembre 2004 3:04 15

304

Au cur de Java 2 - Notions fondamentales

Figure 7.11
Rectangles et ellipses.

Exemple 7.4 : DrawTest.java


import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class DrawTest
{
public static void main(String[] args)
{
DrawFrame frame = new DrawFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre contenant un panneau avec des dessins
*/
class DrawFrame extends JFrame
{
public DrawFrame()
{
setTitle("DrawTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter un panneau au cadre
DrawPanel panel = new DrawPanel();
add(panel);
}
public static final int DEFAULT_WIDTH = 400;
public static final int DEFAULT_HEIGHT = 400;
}

Livre Java .book Page 305 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

/**
Un panneau qui affiche des rectangles et des ellipses.
*/
class DrawPanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
// dessiner un rectangle
double
double
double
double

leftX = 100;
topY = 100;
width = 200;
height = 150;

Rectangle2D rect = new Rectangle2D.Double(leftX, topY,


width, height);
g2.draw(rect);
// dessiner lellipse lintrieur
Ellipse2D ellipse = new Ellipse2D.Double();
ellipse.setFrame(rect);
g2.draw(ellipse);
// tracer une ligne diagonale
g2.draw(new Line2D.Double(leftX, topY,
leftX+width, topY+height));
// dessiner un cercle ayant le mme centre
double centerX = rect.getCenterX();
double centerY = rect.getCenterY();
double radius = 150;
Ellipse2D circle = new Ellipse2D.Double();
circle.setFrameFromCenter(centerX, centerY,
centerX+radius, centerY+radius);
g2.draw(circle);
}
}
java.awt.geom.RectangularShape 1.2

double getCenterX()
double getCenterY()
double getMinX()
double getMinY()
double getMaxX()
double getMaxY()

Renvoient le centre, les valeurs x ou y minimum ou maximum du rectangle englobant.

305

Livre Java .book Page 306 Jeudi, 25. novembre 2004 3:04 15

306

Au cur de Java 2 - Notions fondamentales

double getWidth()
double getHeight()

Renvoient la largeur ou la hauteur du rectangle englobant.

double getX()
double getY()

Renvoient les coordonnes x ou y du coin suprieur gauche du rectangle englobant.


java.awt.geom.Rectangle2D.Double 1.2
Rectangle2D.Double(double x, double y, double w, double h)

Construit un rectangle avec les valeurs donnes pour le coin suprieur gauche, la largeur et la hauteur.
java.awt.geom.Rectangle2D.Float 1.2
Rectangle2D.Float(float x, float y, float w, float h)

Construit un rectangle avec les valeurs donnes pour le coin suprieur gauche, la largeur et la hauteur.
java.awt.geom.Ellipse2D.Double 1.2
Ellipse2D.Double(double x, double y, double w, double h)

Construit une ellipse dont le rectangle englobant a les valeurs donnes pour le coin suprieur
gauche, la largeur et la hauteur.
java.awt.geom.Point2D.Double 1.2
Point2D.Double(double x, double y)

Construit un point avec les coordonnes indiques.


java.awt.geom.Line2D.Double 1.2
Line2D.Double(Point2D start, Point2D end)
Line2D.Double(double startX, double startY, double endX, double endY)

Construisent une ligne avec les points de dpart et darrive indiqus.

Couleurs
La mthode setPaint de la classe Graphics2D permet de slectionner une couleur qui sera
employe par toutes les oprations de dessin ultrieures pour le contexte graphique. Pour dessiner
avec plusieurs couleurs, vous devez slectionner une couleur, effectuer une opration, puis slectionner
une autre couleur avant de procder lopration suivante.
Les couleurs sont dfinies laide de la classe Color. La classe java.awt.Color propose des constantes
prdfinies pour les treize couleurs standard indiques dans le Tableau 7.1.
Tableau 7.1 : Couleurs standard

BLACK (noir)
BLUE (bleu)
CYAN
DARK_GRAY (gris fonc)
GRAY (gris)

GREEN (vert)
LIGHT_GRAY (gris clair)
MAGENTA
ORANGE
PINK (rose)

RED (rouge)
WHITE (blanc)
YELLOW (jaune)

Livre Java .book Page 307 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

307

Par exemple :
g2.setPaint(Color.red);
g2.drawString("Warning!", 100, 100);

INFO
Avant le JDK 1.4, les noms constants des couleurs taient en minuscules, comme Color.red. Ceci est trange car la
convention de codage standard consiste crire les noms constants en majuscules. Depuis le JDK 1.4, vous pouvez
crire les noms des couleurs standard en majuscules ou, pour une compatibilit avec les versions antrieures, en
minuscules.

Vous pouvez spcifier une couleur personnalise en crant un objet Color laide de ses composantes rouge, verte et bleue. En utilisant une chelle de 0 255 (cest--dire un octet) pour les proportions
de rouge, de vert et de bleu, appelez le constructeur de Color de la faon suivante :
Color(int redness, int greenness, int blueness)

Voici un exemple de dfinition de couleur personnalise :


g2.setPaint(new Color(0, 128, 128)); // un bleu-vert fonc
g2.drawString("Welcome!", 75, 125);

INFO
En plus des couleurs franches, vous pouvez slectionner des dfinitions de "peinture" plus complexes, comme des
images ou des teintes nuances. Consultez le Chapitre 7 du Volume 2 traitant de AWT pour plus de dtails. Si vous
utilisez un objet Graphics au lieu de Graphics2D, vous devrez avoir recours la mthode setColor pour dfinir
les couleurs.

Pour spcifier la couleur darrire-plan (ou de fond), utilisez la mthode setBackground de la


classe Component, qui est un anctre de JPanel:
MyPanel p = new MyPanel();
p.setBackground(Color.PINK);

Il existe aussi une mthode setForeground. Elle spcifie la couleur par dfaut utilise pour le
dessin.
ASTUCE
Les mthodes brighter() et darker() de la classe Color produisent des versions plus vives ou plus fonces de la
couleur actuelle. La mthode brighter permet de mettre un lment en surbrillance, mais avive en ralit peine
la couleur. Pour obtenir une couleur nettement plus visible, appelez trois fois la mthode :

c.brighter().brighter().brighter().

Java fournit des noms prdfinis pour de nombreuses couleurs dans sa classe SystemColor. Les
constantes de cette classe encapsulent les couleurs employes pour divers lments du systme de
lutilisateur. Par exemple,
frame.setBackground(SystemColor.window)

Livre Java .book Page 308 Jeudi, 25. novembre 2004 3:04 15

308

Au cur de Java 2 - Notions fondamentales

affecte larrire-plan du panneau la couleur utilise par dfaut par toutes les fentres du bureau
(larrire-plan est rempli avec cette couleur chaque fois que la fentre est repeinte). Lemploi des
couleurs de la classe SystemColor est particulirement utile si vous voulez dessiner des lments
dinterface dont les couleurs doivent saccorder avec celles de lenvironnement. Le Tableau 7.2
dcrit les couleurs systme.
Tableau 7.2 : Couleurs du systme

desktop

Arrire-plan du bureau

activeCaption

Arrire-plan des titres

activeCaptionText

Texte des titres

activeCaptionBorder

Bordure des titres

inactiveCaption

Arrire-plan des titres inactifs

inactiveCaptionText

Texte des titres inactifs

inactiveCaptionBorder

Bordure des titres inactifs

window

Arrire-plan des fentres

windowBorder

Bordure des fentres

windowText

Texte lintrieur dune fentre

menu

Arrire-plan des menus

menuText

Texte des menus

text

Arrire-plan du texte

textText

Couleur du texte

textHighlight

Arrire-plan du texte en surbrillance (marqu)

textHighlightText

Couleur du texte en surbrillance (marqu)

control

Arrire-plan des contrles

controlText

Texte des contrles

controlLtHighlight

Couleur de surbrillance claire des contrles

controlHighlight

Couleur de surbrillance des contrles

controlShadow

Ombre des contrles

controlDkShadow

Ombre fonce des contrles

inactiveControlText

Texte des contrles inactifs

Livre Java .book Page 309 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

309

Tableau 7.2 : Couleurs du systme (suite)

scrollbar

Arrire-plan des barres de dfilement

info

Arrire-plan du texte des bulles daide

infoText

Couleur du texte des bulles daide

java.awt.Color 1.0

Color(int r, int g, int b)

Cre un objet couleur.


Paramtres :

Valeur de la composante rouge (0-255).

Valeur de la composante verte (0-255).

Valeur de la composante bleue (0-255).

java.awt.Graphics 1.0

void setColor(Color c)

Modifie la couleur courante. Toutes les oprations graphiques ultrieures utiliseront la nouvelle
couleur.
Paramtres :

Nouvelle couleur.

java.awt.Graphics2D 1.2

void setPaint(Paint p)

Dfinit les paramtres de peinture de ce contexte graphique. La classe Color implmente linterface Paint. Vous pouvez donc utiliser cette mthode pour affecter les attributs dune couleur.
java.awt.Component 1.0

void setBackground (Color c)

Spcifie la couleur darrire-plan.


Paramtres :

Nouvelle couleur darrire-plan.

void setForeground(Color c)

Spcifie la couleur davant-plan.


Paramtres :

Nouvelle couleur davant-plan.

Remplir des formes


Vous pouvez remplir des formes fermes (comme des rectangles et des ellipses) avec une couleur
(ou, plus gnralement, avec les motifs correspondant la configuration actuelle). Appelez simplement
fill au lieu de draw:
Rectangle2D rect = . . .;
g2.setPaint(Color.RED);
g2.fill(rect); // emplit rect avec la couleur rouge

Livre Java .book Page 310 Jeudi, 25. novembre 2004 3:04 15

310

Au cur de Java 2 - Notions fondamentales

Le programme de lExemple 7.5 peint un rectangle en rouge, puis une ellipse avec les mmes limites,
en vert fonc (voir Figure 7.12).
Figure 7.12
Rectangles
et ellipses colors.

Exemple 7.5 : FillTest.java


import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class FillTest
{
public static void main(String[] args)
{
FillFrame frame = new FillFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre contenant un panneau avec des dessins
*/
class FillFrame extends JFrame
{
public FillFrame()
{
setTitle("FillTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter un panneau au cadre

Livre Java .book Page 311 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

311

FillPanel panel = new FillPanel();


add(panel);
}
public static final int DEFAULT_WIDTH = 400;
public static final int DEFAULT_HEIGHT = 400;
}
/**
Un panneau affichant des rectangles et ellipses colors
*/
class FillPanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
// dessiner un rectangle
double
double
double
double

leftX = 100;
topY = 100;
width = 200;
height = 150;

Rectangle2D rect = new Rectangle2D.Double(leftX, topY,


width, height);
g2.setPaint(Color.RED);
g2.fill(rect);
// dessiner lellipse englobe
Ellipse2D ellipse = new Ellipse2D.Double();
ellipse.setFrame(rect);
g2.setPaint(new Color(0, 128, 128)); // bleu-vert fonc
g2.fill(ellipse);
}
}

Texte et polices
Le programme NotHelloWorld au dbut de ce chapitre affichait une chane avec la fonte par dfaut.
Il est possible dcrire un texte avec une fonte diffrente. Une fonte est spcifie grce son nom de
police. Un nom de police se compose dun nom de famille de polices, comme "Helvetica" et dun
suffixe facultatif, comme "Bold" (gras) ou "Italic" (italique). Autrement dit, les polices "Helvetica",
"Helvetica Bold" et "Helvetica Italic" font toutes partie de la famille "Helvetica".
Pour connatre les fontes disponibles sur un ordinateur, appelez la mthode getAvailableFontFamilyNames de la classe GraphicsEnvironment. Cette mthode renvoie un tableau de chanes contenant les
noms de toutes les fontes disponibles. La mthode statique getLocalGraphicsEnvironment permet
dobtenir une instance de la classe GraphicsEnvironment qui dcrit lenvironnement graphique du

Livre Java .book Page 312 Jeudi, 25. novembre 2004 3:04 15

312

Au cur de Java 2 - Notions fondamentales

systme utilisateur. Le programme suivant donne en sortie une liste de tous les noms de fonte de votre
systme :
import java.awt.*;
public class ListFonts
{
public static void main(String[] args)
{
String[] fontNames = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames();
for (String fontName: fontNames)
System.out.println(fontName);
}
}

Pour un systme donn, la liste commencera ainsi :


Abadi MT Condensed Light
Arial
Arial Black
Arial Narrow
Arioso
Baskerville
Binner Gothic
. . .

et se poursuivra avec quelques 70 fontes supplmentaires.


INFO
La documentation JDK indique que des suffixes comme "heavy", "medium", "oblique" ou "gothic" sont des variantes au sein dune mme famille. Selon notre propre exprience, ce nest pas le cas. Les suffixes "Bold", "Italic" et
"Bold Italic" sont effectivement reconnus comme tels, mais pas les autres.

Il nexiste malheureusement aucun moyen absolu de savoir si un utilisateur a une fonte avec un look
particulier installe. Les noms de fontes peuvent tre dposs, et faire lobjet de copyright sous
certaines juridictions. La distribution des fontes implique donc souvent le versement de droits
dauteur. Bien entendu, comme dans le cas des parfums clbres, on trouve des imitations peu
coteuses de certaines fontes rputes. A titre dexemple, limitation dHelvetica distribue avec
Windows se nomme Arial.
Pour tablir une ligne de base commune, AWT dfinit cinq noms de fontes logiques :
SansSerif
Serif
Espacement fixe
Dialog
DialogInput
Ces noms sont toujours transcrits en fontes existant dans lordinateur client. Par exemple, dans un
systme Windows, SansSerif est transcrit en Arial.

Livre Java .book Page 313 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

313

INFO
La concordance des polices est dfinie dans le fichier fontconfig.properties du sous-rpertoire jre/lib
de linstallation Java. Suivez http://java.sun.com/j2se/5.0/docs/guide/intl/fontconfig.html pour en savoir plus
sur ce fichier. Les versions prcdentes du JDK utilisaient un fichier font.properties qui est maintenant
obsolte.

Pour crire des caractres dans une fonte, vous devez dabord crer un objet de la classe Font. Vous
spcifiez le nom de fonte, son style, et la taille en points. Voici un exemple de construction dun objet
Font:
Font helvb14 = new Font("Helvetica", Font.BOLD, 14);

Le troisime paramtre est la taille en points. Le point est souvent utilis en typographie pour indiquer la
taille dune police. Un pouce comprend 72 points.
Le nom de fonte logique peut tre employ dans le constructeur de Font. Il faut ensuite spcifier le
style (plain, bold, italic ou bold italic) dans le second paramtre du constructeur, en lui donnant une
des valeurs suivantes :
Font.PLAIN
Font.BOLD
Font.ITALIC
Font.BOLD+Font.ITALIC

Par exemple :
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14)

INFO
Les versions prcdentes de Java utilisaient les noms Helvetica, TimesRoman, Courier et ZapfDingbats comme
noms de fontes logiques. Pour des raisons de compatibilit amont, ces noms de fontes sont toujours traits comme
des noms de fontes logiques, bien quHelvetica soit en fait un nom de police et que TimesRoman et ZapfDingbats
ne soient pas des fontes leurs noms de police sont "Times Roman" et "Zapf Dingbats".

ASTUCE
A partir de la version 1.3 du JDK, vous pouvez lire les fontes TrueType. Un flux dentre est ncessaire pour la fonte
gnralement partir dun fichier disque ou dune adresse URL (voir le Chapitre 12 pour plus dinformations sur
les flux). Appelez ensuite la mthode statique Font.createFont:
URL url = new URL("http://www.fonts.com/Wingbats.ttf");
InputStream in = url.openStream();
Font f = Font.createFont(Font.TRUETYPE_FONT, in);

La fonte est normale (plain) avec une taille de 1 point. Employez la mthode deriveFont pour obtenir une fonte
de la taille dsire :
Font df = f.deriveFont(14.0F);

Livre Java .book Page 314 Jeudi, 25. novembre 2004 3:04 15

314

Au cur de Java 2 - Notions fondamentales

ATTENTION
Il existe deux versions surcharges de la mthode deriveFont. Lune delles (ayant un paramtre float) dfinit
le corps de la police, lautre (avec un paramtre int), son style. Ds lors, f.deriveFont(14) dfinit le style et non le
corps (le rsultat est une police italique car la reprsentation binaire de 14 dfinit le type ITALIC mais non le type
BOLD).

Les fontes Java contiennent les habituels caractres ASCII et des symboles. Par exemple, si vous
affichez le caractre \u2297 de la fonte Dialog, vous obtenez un caractre reprsentant une
croix dans un cercle. Seuls sont disponibles les symboles dfinis dans le jeu de caractres Unicode.
Voici maintenant le code affichant la chane "Hello, World!" avec la fonte sans srif standard de
votre systme, en utilisant un style gras en 14 points :
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14);
g2.setFont(sansbold14);
String message = "Hello, World!";
g2.drawString(message, 75, 100);

Nous allons maintenant centrer la chane dans le panneau au lieu de lcrire une position arbitraire.
Nous devons connatre la largeur et la hauteur de la chane en pixels. Ces dimensions dpendent de
trois facteurs :
m

La fonte utilise (ici, sans srif, bold, 14 points).

La chane (ici, "Hello, World!").

Lunit sur laquelle la chane est crite (ici, lcran de lutilisateur).

Pour obtenir un objet qui reprsente les caractristiques de fonte de lcran, appelez la mthode
getFontRenderContext de la classe Graphics2D. Elle renvoie un objet de la classe FontRenderContext. Vous passez simplement cet objet la mthode getStringBounds de la classe Font:
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);

La mthode getStringBounds renvoie un rectangle qui englobe la chane.


Pour interprter les dimensions de ce rectangle, il est utile de connatre certains des termes de typographie (voir Figure 7.13). La ligne de base (baseline) est la ligne imaginaire sur laquelle saligne la
base des caractres comme "e". Le jambage ascendant (ascent) reprsente la distance entre la ligne
de base et la partie suprieure dune lettre "longue du haut", comme "b" ou "k" ou un caractre
majuscule. Le jambage descendant (descent) reprsente la distance entre la ligne de base et la partie
infrieure dune lettre "longue du bas", comme "p" ou "g".
Figure 7.13
Illustration
des termes de
typographie.

baseline
height
baseline

e b k p g

ascent
descent
leading

Livre Java .book Page 315 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

315

Linterligne (leading) est lintervalle entre la partie infrieure dune ligne et la partie suprieure de la
ligne suivante. La hauteur (height) est la distance verticale entre deux lignes de base successives et
quivaut (jambage descendant + interligne + jambage ascendant).
La largeur (width) du rectangle que renvoie la mthode getStringBounds est ltendue horizontale
de la chane. La hauteur du rectangle est la somme des jambages ascendant, descendant et de linterligne. Lorigine du rectangle se trouve la ligne de base de la chane. La coordonne y suprieure du
rectangle est ngative. Vous pouvez obtenir les valeurs de largeur, de hauteur et des jambage ascendant
dune chane, de la faon suivante :
double stringWidth = bounds.getWidth();
double stringHeight = bounds.getHeight();
double ascent = -bounds.getY();

Si vous devez connatre le jambage descendant ou linterligne, appelez la mthode getLineMetrics


de la classe Font. Elle renvoie un objet de la classe LineMetrics, possdant des mthodes permettant
dobtenir le jambage descendant et linterligne :
LineMetrics metrics = f.getLineMetrics(message, context);
float descent = metrics.getDescent();
float leading = metrics.getLeading();

Le code suivant utilise toutes ces informations pour centrer une chane lintrieur de son panneau
contenant :
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);
// (x,y) = coin suprieur gauche du texte
double x = (getWidth() - bounds.getWidth()) / 2;
double y = (getHeight() - bounds.getHeight()) / 2;
// ajouter le jambage ascendant y pour atteindre la ligne de base
double ascent = -bounds.getY();
double baseY = y+ascent;
g2.drawString(message, (int)x, (int)(baseY);

Pour comprendre le centrage, considrez que getWidth() renvoie la largeur du panneau. Une
portion de cette largeur, bounds.getWidth(), est occupe par la chane de message. Le reste doit
tre quitablement rparti des deux cts. Par consquent, lespace vide de chaque ct reprsente la
moiti de la diffrence. Le mme raisonnement sapplique la hauteur.
Enfin, le programme dessine la ligne de base et le rectangle englobant.
La Figure 7.14 montre laffichage lcran ; lExemple 7.6 le listing du programme.
Figure 7.14
Dessin de la ligne
de base et des limites
de la chane.

Livre Java .book Page 316 Jeudi, 25. novembre 2004 3:04 15

316

Au cur de Java 2 - Notions fondamentales

Exemple 7.6 : FontTest.java


import
import
import
import

java.awt.*;
java.awt.font.*;
java.awt.geom.*;
javax.swing.*;

public class FontTest


{
public static void main(String[] args)
{
FontFrame frame = new FontFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau pour un message texte
*/
class FontFrame extends JFrame
{
public FontFrame()
{
setTitle("FontTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter un panneau au cadre
FontPanel panel = new FontPanel();
add(panel);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
/**
Un panneau affichant un message centr dans une bote.
*/
class FontPanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
String message = "Hello, World!";
Font f = new Font("Serif", Font.BOLD, 36);
g2.setFont(f);
// mesurer la taille du message
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);
// dfinir (x,y) = coin suprieur gauche du texte

Livre Java .book Page 317 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

317

double x = (getWidth() - bounds.getWidth()) / 2;


double y = (getHeight() - bounds.getHeight()) / 2;
/* ajouter jambage ascendant y pour
atteindre la ligne de base */
double ascent = -bounds.getY();
double baseY = y+ascent;
// crire le message
g2.drawString(message, (int)x, (int)(baseY);
g2.setPaint(Color.GRAY);
// dessiner la ligne de base
g2.draw(new Line2D.Double(x, baseY,
x+bounds.getWidth(), baseY));
// dessiner le rectangle englobant
Rectangle2D rect = new Rectangle2D.Double(x, y,
bounds.getWidth(),
bounds.getHeight());
g2.draw(rect);
}
}
java.awt.Font 1.0
Font(String name, int style, int size)

Cre un nouvel objet fonte.


Paramtres :

name

Le nom de la fonte. Il sagit, soit dun nom de police


(comme "Helvetica Bold"), soit dun nom de fonte logique
(comme "Serif" ou "SansSerif").

style

Le style (Font.PLAIN, Font.BOLD, Font.ITALIC


ou Font.BOLD+Font.ITALIC).

size

La taille en points (par exemple, 12).

String getFontName()

Renvoie le nom de police (par exemple, "Helvetica Bold").

String getFamily()

Renvoie le nom de la famille de polices (comme "Helvetica").

String getName()

Renvoie le nom logique (par exemple, "SansSerif") si la fonte a t cre avec un nom de police
logique ; sinon la mthode renvoie le "nom de police" de la fonte.

Rectangle2D getStringBounds(String s, FontRenderContext context) 1.2

Renvoie un rectangle qui englobe la chane. Lorigine du rectangle se trouve sur la ligne de base.
La coordonne y suprieure du rectangle est gale la valeur ngative du jambage ascendant. La
hauteur du rectangle gale la somme des jambages ascendant et descendant et de linterligne.
La largeur est celle de la chane.

Livre Java .book Page 318 Jeudi, 25. novembre 2004 3:04 15

318

Au cur de Java 2 - Notions fondamentales

LineMetrics getLineMetrics(String s, FontRenderContext context) 1.2

Renvoie un objet Line pour dterminer ltendue de la chane.

Font deriveFont(int style) 1.2


Font deriveFont(float size) 1.2
Font deriveFont(int style, float size) 1.2

Renvoient une nouvelle fonte quivalant cette fonte, avec la taille et le style demands.
java.awt.font.LineMetrics 1.2

float getAscent()

Renvoie la taille du jambage ascendant distance entre la ligne de base et le sommet des caractres majuscules.

float getDescent()

Renvoie la taille du jambage descendant distance entre la ligne de base et le bas des lettres
"longues du bas", comme "p" ou "g".

float getLeading()

Renvoie linterligne lespace entre la partie infrieure dune ligne de texte et la partie suprieure de la ligne suivante.

float getHeight()

Renvoie la hauteur totale de la fonte distance entre deux lignes de base (jambage descendant + interligne + jambage ascendant).
java.awt.Graphics 1.0

void setFont(Font font)

Slectionne une fonte pour le contexte graphique. Cette fonte sera employe dans les oprations
ultrieures daffichage de texte.
Paramtres :

font

Une fonte.

void drawString(String str, int x, int y)

Dessine une chane avec la fonte et la couleur courantes.


Paramtres :

str

La chane dessiner.

Coordonne x du dbut de la chane.

Coordonne y de la ligne de base de la chane.

java.awt.Graphics2D 1.2

FontRenderContext getFontRenderContext ()

Extrait un contexte de rendu de fonte qui spcifie les caractristiques de la fonte dans ce contexte
graphique.

void drawString(String str, float x, float y)

Dessine une chane avec la fonte et la couleur courantes.


Paramtres :

str

La chane dessiner.

Coordonne x du dbut de la chane.

Coordonne y de la ligne de base de la chane.

Livre Java .book Page 319 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

319

Images
Vous avez vu comment crer des images simples en traant des lignes et des formes. Des images
complexes, telles que des photographies, sont habituellement gnres en externe, par exemple avec
un scanner ou un logiciel ddi la manipulation dimages (comme vous le verrez au Chapitre 7 du
Volume 2, il est galement possible de produire une image pixel par pixel, et de stocker le rsultat
dans un tableau. Cette procdure est courante pour les images fractales, par exemple).
Lorsque des images sont stockes dans des fichiers locaux ou sur Internet, vous pouvez les lire dans
une application Java et les afficher sur des objets de type Graphics. Depuis le JDK 1.4, la lecture
dune image est trs simple. Si limage est stocke dans un fichier local, appelez
String filename = "...";
Image image = ImageIO.read(new File(filename));

Sinon vous pouvez fournir une URL :


String urlname = "...";
Image image = ImageIO.read(new URL(urlname));

La mthode de lecture dclenche une exception IOException si limage nest pas disponible. Nous
traiterons du sujet gnral de la gestion des exceptions au Chapitre 11. Pour linstant, notre exemple
de programme se contente dintercepter cette exception et affiche un stack trace le cas chant.
La variable image contient alors une rfrence un objet qui encapsule les donnes image. Vous
pouvez afficher limage grce la mthode drawImage de la classe Graphics:
public void paintComponent(Graphics g)
{
. . .
g.drawImage(image, x, y, null);
}

LExemple 7.7 va un peu plus loin et affiche limage en mosaque dans la fentre. Le rsultat ressemble celui de la Figure 7.15. Laffichage en mosaque est effectu dans la mthode paintComponent. Nous dessinons dabord une copie de limage dans le coin suprieur gauche, puis nous
appelons copyArea pour la copier dans toute la fentre :
for (int i = 0; i * imageWidth <= getWidth(); i++)
for (int j = 0; j * imageHeight <= getHeight(); j++)
if (i+j > 0)
g.copyArea(0, 0, imageWidth, imageHeight,
i * imageWidth, j * imageHeight);

Figure 7.15
Affichage dune image
en mosaque dans
une fentre.

Livre Java .book Page 320 Jeudi, 25. novembre 2004 3:04 15

320

Au cur de Java 2 - Notions fondamentales

INFO
Pour charger une image avec le JDK 1.3 ou version antrieure, utilisez plutt la classe MediaTracker. Un "pisteur
de mdia" peut suivre lacquisition dune ou plusieurs images (le terme "mdia" laisse supposer que cette classe peut
suivre galement des fichiers audio ou dautres supports. Ce sera peut-tre le cas grce une extension, mais pour
linstant la classe ne peut suivre que des images).
Nous ajoutons une image un objet pisteur avec la commande suivante :
MediaTracker tracker = new MediaTracker(component);
Image img = Toolkit.getDefaultToolkit().getImage(name);
int id = 1; // lID pour pister le processus de chargement dimage
tracker.addImage(img, id);

Nous pouvons ajouter autant dimages que nous le souhaitons un seul objet MediaTracker. Chaque image doit
avoir un numro didentification (ID) diffrent, mais nous pouvons choisir nous-mmes ce numro. Pour attendre le
chargement complet dune image, nous employons des instructions comparables celles-ci :
try { tracker.waitForID(id); }
catch (InterruptedException e) {}

Si vous dsirez charger plusieurs images, vous pouvez toutes les ajouter lobjet MediaTracker et demander au
programme dattendre quelles soient toutes charges :
try { tracker.waitForAll(); }
catch (InterruptedException e) {}

LExemple 7.7 prsente le code source du programme de dmonstration permettant dafficher une
image. Ceci conclut notre introduction sur la programmation du graphisme dans Java. Pour dcouvrir
des techniques plus avances, dcouvrez le graphisme et les images 2D dans le Volume 2.
Exemple 7.7 : ImageTest.java
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.io.*;
javax.imageio.*;
javax.swing.*;

public class ImageTest


{
public static void main(String[] args)
{
ImageFrame frame = new ImageFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau pour une image
*/
class ImageFrame extends JFrame
{
public ImageFrame()
{
setTitle("ImageTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

Livre Java .book Page 321 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

// ajouter un panneau au cadre


ImagePanel panel = new ImagePanel();
add(panel);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
/**
Un panneau qui affiche une image en mosaque
*/
class ImagePanel extends JPanel
{
public ImagePanel()
{
// acqurir limage
try
{
image = ImageIO.read(new File("blue-ball.gif"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (image == null) return;
int imageWidth = image.getWidth(this);
int imageHeight = image.getHeight(this);
// dessiner limage dans le coin suprieur gauche
g.drawImage(image, 0, 0, null);
// afficher limage en mosaque dans le panneau
for (int i = 0; i * imageWidth <= getWidth(); i++)
for (int j = 0; j * imageHeight <= getHeight(); j++)
if (i+j > 0)
g.copyArea(0, 0, imageWidth, imageHeight,
i * imageWidth, j * imageHeight);
}
private Image image;
}
javax.swing.ImageIO 1.4

static BufferedImage read(File f)


static BufferedImage read(URL u)

Renvoient une image partir du fichier donn ou de lURL.

321

Livre Java .book Page 322 Jeudi, 25. novembre 2004 3:04 15

322

Au cur de Java 2 - Notions fondamentales

java.awt.Image 1.0

Graphics getGraphics()

Rcupre un contexte graphique pour dessiner dans ce tampon dimage.

void flush()

Libre toutes les ressources dtenues par cet objet image.


java.awt.Graphics 1.0

boolean drawImage(Image img, int x, int y, ImageObserver observer)

Dessine une image non mise lchelle. Remarque : cette mthode peut renvoyer un rsultat
avant que limage soit compltement dessine.
Paramtres :

img

Limage dessiner.

Coordonne x du coin suprieur gauche.

Coordonne y du coin suprieur gauche.

observer

Lobjet qui doit tre notifi en ce qui concerne la progression


de laffichage (cette valeur peut tre null).

boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver
observer)

Dessine une image mise lchelle. Le systme adapte la taille de limage afin quelle occupe
une zone ayant la largeur et la hauteur spcifies. Remarque : cette mthode peut renvoyer un
rsultat avant que limage ne soit compltement dessine.
Paramtres :

img

Limage dessiner.

Coordonne x du coin suprieur gauche.

Coordonne y du coin suprieur gauche.

width

Largeur souhaite pour limage.

height

Hauteur souhaite pour limage.

observer

Lobjet qui doit tre notifi en ce qui concerne la progression


de laffichage (cette valeur peut tre null).

void copyArea(int x, int y, int width, int height, int dx, int dy)

Copie une zone rectangulaire de lcran.


Paramtres :

Coordonne x du coin suprieur gauche de la zone source.

Coordonne y du coin suprieur gauche de la zone source.

width

Largeur de la zone source.

height

Hauteur de la zone source.

dx

Distance horizontale entre la zone source et la zone cible.

dy

Distance verticale entre la zone source et la zone cible.

void dispose()

Libre ce contexte graphique et les ressources associes du systme dexploitation. Il faut


toujours librer les contextes graphiques obtenus par des appels des mthodes comme
Image.getGraphics, mais pas les contextes fournis par paintComponent.

Livre Java .book Page 323 Jeudi, 25. novembre 2004 3:04 15

Chapitre 7

Programmation graphique

323

java.awt.Component 1.0
Image createImage(int width, int height)

Cre un tampon dimage hors cran qui sera employ par le mcanisme de double tampon
(double buffering).
Paramtres :

width

Largeur de limage.

height

Hauteur de limage.

java.awt.MediaTracker 1.0
MediaTracker(Component c)

Piste les images qui sont affiches dans le composant donn.

void addImage(Image image, int id)

Ajoute une image la liste des images "pistes" (ou "suivies"). Le processus de chargement de
cette image dmarre ds quelle est ajoute la liste.
Paramtres :

image

Limage pister.

id

Identificateur employ pour les rfrences ultrieures cette


image.

void waitForID(int id)

Attend que toutes les images ayant lidentificateur spcifi soient charges.

void waitForAll()

Attend que toutes les images "pistes" soient charges.

Livre Java .book Page 324 Jeudi, 25. novembre 2004 3:04 15

Livre Java .book Page 325 Jeudi, 25. novembre 2004 3:04 15

8
Gestion des vnements
Au sommaire de ce chapitre

Introduction la gestion des vnements


Hirarchie des vnements AWT
Evnements smantiques et de bas niveau
Types dvnements de bas niveau
Actions
Multidiffusion
Mise en place des sources dvnements
La gestion des vnements est dune importance capitale pour les programmes ayant une interface.
Pour implmenter des interfaces utilisateur graphiques, vous devez matriser la gestion des vnements. Ce chapitre explique le fonctionnement du modle dvnement AWT. Vous apprendrez
capturer les vnements en provenance de la souris et du clavier, ainsi que lusage des lments les
plus simples dune interface, comme les boutons. Nous verrons en particulier comment utiliser les
vnements de base gnrs par ces composants. Le chapitre suivant vous montrera comment intgrer une application les autres composants proposs par Swing et examinera en dtail les vnements
quils gnrent.
INFO
Java 1.1 a introduit un modle dvnement trs amlior pour la programmation dune GUI. Nous ne parlerons pas
du modle dvnement dprci 1.0 dans ce chapitre.

Livre Java .book Page 326 Jeudi, 25. novembre 2004 3:04 15

326

Au cur de Java 2 - Notions fondamentales

Introduction la gestion des vnements


Tout environnement dexploitation qui gre les interfaces utilisateur graphiques doit constamment dtecter les vnements tels que la pression sur une touche du clavier ou sur un bouton de
la souris. Lenvironnement dexploitation en informe alors les programmes en cours dexcution.
Chaque programme dtermine ensuite sil doit rpondre ces vnements. Dans des langages
comme Visual Basic, la correspondance entre les vnements et le code est vidente. Le
programmeur crit une routine pour chaque vnement digne dintrt et place ce code dans ce
que lon appelle une procdure dvnement. Par exemple, un bouton nomm HelpButton serait
associ une procdure dvnement HelpButton_Click. Le code de cette procdure sexcute
en rponse un clic de la souris sur ce bouton. En Visual Basic, chaque composant dinterface
rpond un ensemble fixe dvnements, et il est impossible de modifier les vnements
auxquels il peut rpondre.
En revanche, si vous employez un langage comme le C pur pour faire de la programmation
vnementielle, vous devez crire le code qui vrifie constamment la file dvnements (en gnral, ce code est imbriqu dans une boucle gante contenant une norme instruction switch !).
Cette technique est videmment peu lgante et difficile programmer. Elle a cependant un
avantage : les vnements auxquels lapplication peut rpondre ne sont pas limits comme dans
les langages qui sefforcent de dissimuler la file dvnements au programmeur (tel Visual
Basic).
Lenvironnement de programmation Java a choisi une approche intermdiaire entre celle du C et
celle de Visual Basic, ce qui augmente sa complexit. Dans les limites des vnements connus
dAWT, vous contrlez compltement la manire dont les vnements sont transmis de la source
dvnement (par exemple, un bouton ou une barre de dfilement) lcouteur dvnement (event
listener). Vous pouvez dsigner nimporte quel objet comme couteur dvnement dans la pratique, vous choisissez un objet qui est capable de fournir une rponse approprie lvnement. Ce
modle de dlgation dvnement offre une flexibilit bien suprieure celle de Visual Basic, o
lcouteur est prdtermin, mais il exige davantage de code et est plus difficile analyser (tant que
son usage ne vous est pas familier).
Les sources dvnement ont des mthodes qui leur permettent denregistrer (ou de recenser) les
couteurs dvnement. Lorsquun vnement arrive la source, celle-ci envoie une notification
tous les objets couteurs recenss pour cet vnement.
Bien entendu, dans un langage orient objet comme Java, linformation relative lvnement est
encapsule dans un objet vnement. Tous les objets vnement drivent, directement ou indirectement, de la classe java.util.EventObject. Il existe videmment des sous-classes pour chaque
type dvnement, comme ActionEvent et WindowEvent.
Diffrentes sources dvnement peuvent produire diffrentes sortes dvnements. Par exemple,
un bouton peut envoyer des objets ActionEvent alors quune fentre enverra des objets
WindowEvent.

Livre Java .book Page 327 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

327

Pour rsumer, voici en gros comment les vnements sont grs avec AWT :
m

Un objet couteur est une instance dune classe qui implmente une interface spciale appele
interface couteur (listener interface).

Une source dvnement est un objet qui peut recenser des objets couteurs et leur envoyer des
objets vnements.

Lorsquun vnement se produit, la source dvnement envoie lobjet vnement tous les
couteurs recenss.

Les objets couteurs utilisent alors linformation contenue dans lobjet vnement pour dterminer
leur rponse.

Pour recenser lobjet couteur auprs de lobjet source, utilisez une instruction construite sur ce
modle :
ObjetSourceEvnement.addEvnementListener(objetEcouteurEvnement);

Voici un exemple :
ActionListener listener = . . .;
JButton button = new JButton("Ok");
button.addActionListener(listener);

Lobjet listener recevra dsormais une notification chaque fois quun "vnement Action" concernera le bouton button. Dans le cas dun bouton, un vnement Action est un clic de la souris sur ce
bouton.
Lexemple prcdent exige que la classe laquelle appartient lobjet couteur implmente linterface
approprie (en loccurrence, ActionListener). Comme pour toutes les interfaces Java, limplmentation dune interface signifie quil faut fournir des mthodes ayant la signature correcte. Pour implmenter linterface ActionListener, la classe de lcouteur doit possder une mthode (nomme
actionPerformed) qui recevra lobjet ActionEvent comme paramtre :
class MyListener implements ActionListener
{
. . .
public void actionPerformed(ActionEvent event)
{
// la raction un clic sur le bouton, ici
. . .
}
}

Chaque fois que lutilisateur clique sur le bouton, lobjet JButton cre un objet ActionEvent et
appelle listener.actionPerformed(event) en lui passant cet objet vnement. Il est possible
dajouter plusieurs objets en tant qucouteurs dune source dvnement, par exemple un bouton.
Dans ce cas, le bouton appelle les mthodes actionPerformed de tous les couteurs, chaque fois
que lutilisateur clique sur le bouton.

Livre Java .book Page 328 Jeudi, 25. novembre 2004 3:04 15

328

Au cur de Java 2 - Notions fondamentales

La Figure 8.1 montre linteraction entre la source de lvnement, lcouteur dvnement et lobjet
vnement.
Figure 8.1
Notification
dvnement.

MyPanel

new

new

JButton

MyListener

addActionListener

actionPerformed

INFO
Dans ce chapitre, nous insistons particulirement sur la gestion des vnements de linterface utilisateur tels que les
clics sur des boutons et les dplacements de la souris. Cependant, larchitecture de gestion dvnements de base
nest pas limite aux interfaces utilisateur. Comme vous le verrez au chapitre suivant, les objets qui ne constituent pas
des composants dinterface utilisateur peuvent aussi envoyer des notifications dvnements aux couteurs, gnralement pour leur indiquer quune proprit de lobjet a chang.

Exemple : gestion dun clic de bouton


Pour mieux comprendre le modle de dlgation dvnement, examinons ce quil nous faut pour
construire un simple exemple de rponse un clic sur un bouton :
m

un panneau contenant trois boutons ;

trois objets couteurs qui sont ajouts comme couteurs daction des boutons.

Livre Java .book Page 329 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

329

Dans cet exemple, chaque fois que lutilisateur clique sur un des boutons du panneau, lobjet couteur associ reoit un objet ActionEvent indiquant un clic sur un bouton. Dans notre exemple,
lobjet couteur change alors la couleur darrire-plan du panneau.
Avant de vous montrer le programme lcoute des clics sur les boutons, nous devons expliquer
comment crer des boutons et les ajouter au panneau (pour dautres informations sur les lments
dune interface graphique, reportez-vous au Chapitre 9).
Pour crer un bouton, nous spcifions une chane de libell ou une icne (ou les deux) dans le
constructeur du bouton. Voici deux exemples :
JButton yellowButton = new JButton("Yellow");
JButton blueButton = new JButton(new ImageIcon("blue-ball.gif"));

Les boutons sont ajouts au panneau par des appels une mthode nomme add. Celle-ci reoit en
paramtre le composant qui doit tre ajout au conteneur. Par exemple :
class ButtonPanel extends JPanel
{
public ButtonPanel()
{
JButton yellowButton = new JButton("Yellow");
JButton blueButton = new JButton("Blue");
JButton redButton = new JButton("Red");
add(yellowButton);
add(blueButton);
add(redButton);
}
}

Le rsultat est montr la Figure 8.2.


Figure 8.2
Un panneau contenant
des boutons.

Maintenant que vous savez ajouter des boutons un panneau, il vous faut crire le code qui
permet au panneau dcouter ces boutons. Cela ncessite des classes implmentant linterface
ActionListener qui ne possde quune seule mthode, nomme actionPerformed, dont voici
la signature :
public void actionPerformed(ActionEvent event)

Livre Java .book Page 330 Jeudi, 25. novembre 2004 3:04 15

330

Au cur de Java 2 - Notions fondamentales

INFO
Linterface ActionListener utilise dans notre exemple nest pas limite aux clics sur des boutons. Elle peut tre
utilise dans bien dautres situations, par exemple :

lors de la slection dun lment de menu ;


lors de la slection dun lment dans une zone de liste laide dun double-clic ;
lorsque la touche "Entre" est active dans un champ de texte ;
lorsquun composant Timer dclenche une impulsion aprs lcoulement dun temps donn.
Nous reviendrons sur cette interface dans la suite de ce chapitre et dans le suivant.

ActionListener sutilise de la mme manire dans toutes les situations : sa mthode (unique) actionPerformed
reoit en paramtre un objet de type ActionEvent. Cet objet vnement fournit des informations sur lvnement
qui a t dclench.

Lorsquun bouton est cliqu, nous voulons modifier la couleur darrire-plan du panneau contenant
les boutons, et nous stockons la couleur choisie dans notre classe couteur :
class ColorAction implements ActionListener
{
public ColorAction(Color c)
{
backgroundColor = c;
}
public void actionPerformed(ActionEvent event)
{
// dfinir la couleur darrire-plan du panneau
. . .
}
private Color backgroundColor;
}

Nous construisons ensuite un objet pour chaque couleur et les dfinissons comme couteurs des
boutons :
ColorAction yellowAction = new ColorAction(Color.YELLOW);
ColorAction blueAction = new ColorAction(Color.BLUE);
ColorAction redAction = new ColorAction(Color.RED);
yellowButton.addActionListener(yellowAction);
blueButton.addActionListener(blueAction);
redButton.addActionListener(redAction);

Par exemple, si un utilisateur clique sur un bouton marqu "Yellow", la mthode actionPerformed
de lobjet yellowAction est appele. Son champ dinstance backgroundColor est dfini
Color.YELLOW, et la mthode peut alors poursuivre la dfinition de la couleur darrire-plan du
panneau.
Il reste un problme. Lobjet ColorAction na pas accs la variable panel. Vous pouvez rsoudre
ce problme de deux faons. Vous pouvez stocker le panneau dans lobjet ColorAction et le dfinir
dans le constructeur de ColorAction. Ou, mieux encore, vous pouvez construire lobjet ColorAction dans une classe interne de la classe ButtonPanel. Ses mthodes peuvent alors accder au

Livre Java .book Page 331 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

331

panneau externe automatiquement (pour plus dinformations sur les classes internes, voir le
Chapitre 6).
Nous allons adopter la seconde approche. Voici comment placer la classe ColorAction lintrieur
de la classe ButtonPanel:
class ButtonPanel extends JPanel
{
. . .
private class ColorAction implements ActionListener
{
. . .
public void actionPerformed(ActionEvent event)
{
setBackground(backgroundColor);
// cest--dire outer.setBackground(...)
}
private Color backgroundColor;
}
}

Examinez attentivement la mthode actionPerformed. La classe ColorAction na pas de mthode


setBackground. Mais la classe externe ButtonPanel en possde. Les mthodes sont invoques sur
lobjet ButtonPanel qui a construit les objets de la classe interne (notez bien, ici encore, que outer
nest pas un mot cl du langage de programmation Java. Nous lemployons simplement comme
symbole intuitif pour la rfrence invisible de classe externe dans lobjet de la classe interne).
Cette situation est trs courante. Les objets couteurs dvnement ont habituellement besoin de
raliser une action qui affecte dautres objets. Vous pouvez souvent placer stratgiquement la classe
couteur lintrieur de la classe dont ltat doit tre modifi par lcouteur.
LExemple 8.1 contient la totalit du programme. Chaque fois que vous cliquez sur lun des boutons,
lcouteur daction appropri modifie la couleur darrire-plan du panneau.
Exemple 8.1 : ButtonTest.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonTest
{
public static void main(String[] args)
{
ButtonFrame frame = new ButtonFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau contenant des boutons

Livre Java .book Page 332 Jeudi, 25. novembre 2004 3:04 15

332

Au cur de Java 2 - Notions fondamentales

*/
class ButtonFrame extends JFrame
{
public ButtonFrame()
{
setTitle("ButtonTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter le panneau au cadre
ButtonPanel panel = new ButtonPanel();
add(panel);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
/**
Un panneau avec trois boutons.
*/
class ButtonPanel extends JPanel
{
public ButtonPanel()
{
// crer les boutons
JButton yellowButton = new JButton("Yellow");
JButton blueButton = new JButton("Blue");
JButton redButton = new JButton("Red");
// ajouter les boutons au panneau
add(yellowButton);
add(blueButton);
add(redButton);
// crer les actions des boutons
ColorAction yellowAction = new ColorAction(Color.YELLOW);
ColorAction blueAction = new ColorAction(Color.BLUE);
ColorAction redAction = new ColorAction(Color.RED);
// associer les actions aux boutons
yellowButton.addActionListener(yellowAction);
blueButton.addActionListener(blueAction);
redButton.addActionListener(redAction);
}
/**
Un couteur daction qui dfinit la couleur
darrire-plan du panneau.
*/
private class ColorAction implements ActionListener
{
public ColorAction(Color c)

Livre Java .book Page 333 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

333

{
backgroundColor = c;
}
public void actionPerformed(ActionEvent event)
{
setBackground(backgroundColor);
}
private Color backgroundColor;
}
}
javax.swing.JButton 1.2

JButton(String label)

Construit un bouton. La chane du label peut tre du texte pur, ou HTML partir du JDK 1.3 ;
par exemple, "<html><b>Ok</b></html>".
Paramtres :

label

Le texte du libell affich sur le bouton.

JButton(Icon icon)

Construit un bouton.
Paramtres :

icon

Licne affiche sur le bouton.

JButton(String label, Icon icon)

Construit un bouton.
Paramtres :

label

Le texte du libell affich sur le bouton.

iconLicne affiche sur le bouton.


java.awt.Container 1.0

Component add(Component c)

Ajoute le composant c ce conteneur.


javax.swing.ImageIcon 1.2

ImageIcon(String filename)

Construit une icne dont limage est stocke dans un fichier. Limage est charge automatiquement
avec un pisteur de mdia (media tracker, voir Chapitre 7).

Etre confortable avec les classes internes


Certains dtestent les classes internes, car ils ont limpression quune prolifration de classes et
dobjets ralentit les programmes. Voyons ce quil en est. Vous navez pas besoin dune nouvelle
classe pour chaque composant de linterface utilisateur. Dans notre exemple, les trois boutons partagent la mme classe couteur. Bien sr, chacun deux a un objet couteur spar, mais ces objets ne
sont pas gros. Ils contiennent chacun une valeur de couleur et une rfrence au panneau. Et la solution traditionnelle, avec des instructions if . . . else, rfrence les mmes objets couleur que
ceux stocks par les couteurs daction, comme des variables locales et non comme des champs
dinstance.

Livre Java .book Page 334 Jeudi, 25. novembre 2004 3:04 15

334

Au cur de Java 2 - Notions fondamentales

Il est temps davoir recours aux classes internes. Il est recommand dutiliser des classes internes
ddies pour les gestionnaires dvnement plutt que de transformer les classes existantes en couteurs.
Nous pensons que mme les classes internes anonymes ont leur place.
Voici un bon exemple de la faon dont les classes internes anonymes peuvent rellement simplifier
votre code. Si vous examinez le code de lExemple 8.1, vous verrez que chaque bouton requiert le
mme traitement :
1. construire le bouton avec un libell ;
2. ajouter le bouton au panneau ;
3. construire un couteur daction avec la couleur approprie ;
4. ajouter cet couteur daction.
Implmentons une mthode "assistante" (helper) pour simplifier ces tches :
void makeButton(String name, Color backgroundColor)
{
JButton button = new JButton(name);
add(button);
ColorAction action = new ColorAction(backgroundColor);
button.addActionListener(action);
}

Le constructeur de ButtonPanel devient alors simplement :


public ButtonPanel()
{
makeButton("yellow", Color.YELLOW);
makeButton("blue", Color.BLUE);
makeButton("red", Color.RED);
}

Vous pouvez encore simplifier. Notez que la classe ColorAction nest ncessaire quune fois, dans
la mthode makeButton. Vous pouvez donc en faire une classe anonyme :
void makeButton(String name, final Color backgroundColor)
{
JButton button = new JButton(name);
add(button);
button.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setBackground(backgroundColor);
}
});
}

Le code de lcouteur daction a t simplifi. La mthode actionPerformed fait simplement rfrence la variable paramtre backgroundColor (comme pour toutes les variables locales auxquelles
on accde dans la classe interne, le paramtre doit tre dclar comme final).
Aucun constructeur explicite nest ncessaire. Comme vous avez vu au Chapitre 6, le mcanisme de
classe interne gnre automatiquement un constructeur qui stocke toutes les variables final locales
qui sont utilises dans lune des mthodes de la classe interne.

Livre Java .book Page 335 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

335

ASTUCE
Les classes internes anonymes peuvent paratre droutantes. Mais vous pouvez vous habituer les dchiffrer en vous
entranant analyser le code de la faon suivante :
button.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setBackground(backgroundColor);
}
});

Cest--dire que laction du bouton consiste dfinir une couleur darrire-plan. Tant que le gestionnaire dvnement est constitu simplement de quelques instructions, cela reste trs lisible, en particulier si vous ne vous proccupez pas des mcanismes de classe interne.

ASTUCE
Le JDK 1.4 introduit un mcanisme qui vous permet de spcifier des couteurs dvnement simples sans programmer de classes internes. Supposons par exemple que vous ayez un bouton intitul Load dont le gestionnaire dvnement contient un seul appel de mthode :
frame.loadData();

Vous pouvez bien entendu utiliser une classe interne anonyme :


loadButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
frame.loadData();
}
});

Or la classe EventHandler peut crer automatiquement cet couteur, par lappel


EventHandler.create(ActionListener.class, frame, "loadData")

Vous devrez bien sr installer malgr tout le gestionnaire :


loadButton.addActionListener((ActionListener)
EventHandler.create(ActionListener.class, frame, "loadData"));

Le transtypage est ncessaire car la mthode create renvoie un Object. Une future version du JDK profitera peuttre pleinement des types gnriques, pour rendre cette mthode encore plus commode.
Si lcouteur dvnement appelle une mthode avec un seul paramtre, driv du gestionnaire dvnement, vous
pouvez utiliser une autre forme de la mthode create. Par exemple, lappel
EventHandler.create(ActionListener.class, frame, "loadData", "source.text")

quivaut
new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
frame.loadData(((JTextField) event.getSource()).getText());
}
}

Livre Java .book Page 336 Jeudi, 25. novembre 2004 3:04 15

336

Au cur de Java 2 - Notions fondamentales

Vous remarquerez que le gestionnaire dvnement transforme les noms de la source des proprits et le texte en
appels de mthode getSource et getText, laide de la convention JavaBeans (pour en savoir plus sur les proprits
et les composants JavaBeans, consultez le Volume 2).
Toutefois, dans la pratique, cette situation nest pas frquente, et il nexiste pas de mcanisme pour fournir des paramtres qui ne soient pas drivs de lobjet vnement.

Transformer des composants en couteurs dvnement


Vous tes tout fait libre de dsigner nimporte quel objet dune classe qui implmente linterface
ActionListener comme couteur du bouton. Nous prfrons utiliser les objets dune nouvelle
classe qui a t expressment cre pour raliser les actions souhaites. De nombreux programmeurs
ne sont pas trs laise avec les classes internes et choisissent une stratgie diffrente. Ils retrouvent
le composant qui est modifi en consquence de lvnement, amnent ce composant implmenter
linterface ActionListener et ajoutent une mthode actionPerformed. Dans notre exemple, vous
pouvez transformer ButtonPanel en couteur daction :
class ButtonPanel extends JPanel implements ActionListener
{
. . .
public void actionPerformed(ActionEvent event)
{
// dfinir la couleur darrire-plan
. . .
}
}

Puis le panneau se dfinit lui-mme comme lcouteur des trois boutons :


yellowButton.addActionListener(this);
blueButton.addActionListener(this);
redButton.addActionListener(this);

Notez que, maintenant, les trois boutons nont plus dcouteurs individuels. Ils partagent un unique
objet couteur, le panneau avec les boutons. Par consquent, la mthode actionPerformed doit
dterminer sur quel bouton il a t cliqu.
La mthode getSource de la classe EventObject, la superclasse de toutes les autres classes
dvnement, va vous indiquer la source de chaque vnement. La source de lvnement est lobjet
qui a gnr lvnement et notifi lcouteur :
Object source = event.getSource();

La mthode actionPerformed peut alors vrifier lequel des boutons tait la source :
if (source == yellowButton) . . .
else if (source == blueButton) . . .
else if (source == redButton ) . . .

Cette approche requiert bien entendu que vous conserviez les rfrences aux boutons en tant que
champs dinstance dans le panneau qui les contient.
Comme vous pouvez le voir, transformer le panneau du bouton en couteur daction nest pas forcment plus simple que dfinir une classe interne. Cela devient aussi vraiment encombr lorsque le
panneau contient plusieurs lments dinterface utilisateur.

Livre Java .book Page 337 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

337

ATTENTION
Certains programmeurs utilisent une mthode diffrente pour dterminer la source de lvnement dans un objet
couteur partag par plusieurs sources. La classe ActionEvent possde une mthode getActionCommand qui
renvoie la chane de commande associe cette action. Pour les boutons, il savre que la chane de commande est,
par dfaut, le label du bouton. Si vous adoptez cette approche, une mthode actionPerformed contiendra un
code comme celui-ci :
String command = event.getActionCommand();
if (command.equals("Yellow")) . . .;
else if (command.equals("Blue")) . . .;
else if (command.equals("Red")) . . .;

Nous vous recommandons de ne pas adopter cette approche. Bien sr, se fier aux libells des boutons est dangereux.
Il est trs facile de baptiser un bouton "Gray" puis dpeler la chane de manire lgrement diffrente dans le test :
if (command.equals("Grey")) . . .

De plus, les labels de boutons posent des problmes si vous internationalisez votre application. Pour raliser la
version allemande avec les libells de boutons "Gelb", "Blau" et "Rot", vous devrez modifier la fois les labels de
boutons et les chanes dans la mthode actionPerformed.

java.util.EventObject 1.1

Object getSource()

Renvoie une rfrence sur lobjet o lvnement sest produit.


java.awt.event.ActionEvent 1.1

String getActionCommand()

Renvoie la chane de commande associe cet vnement action. Si lvnement a t dclench


par un bouton, la chane de commande contient le libell du bouton, moins dun changement
intervenu par le biais de la mthode setActionCommand.
java.beans.EventHandler 1.4

static Object create(Class listenerInterface, Object target, String action)

static Object create(Class listenerInterface, Object target, String action,


String eventProperty, String listenerMethod)

static Object create(Class listenerInterface, Object target, String action,


String eventProperty)

Ces mthodes construisent un objet dune classe proxy qui implmente linterface donne. La
mthode nomme ou toutes les mthodes de linterface ralisent laction donne sur lobjet
cible.
Laction peut tre un nom de mthode ou une proprit de la cible. Sil sagit dune proprit, sa
mthode setter est excute. Par exemple, une action "text" est transforme en appel de la
mthode setText.
La proprit dvnement est constitue dun ou de plusieurs noms de proprit spars par un
point. La premire proprit est lue depuis le paramtre de la mthode couteur. La deuxime est
lue partir de lobjet de rsultat, etc. Le rsultat dfinitif devient le paramtre de laction. Par
exemple, la proprit "source.text" est transforme en appels aux mthodes getSource et
getText.

Livre Java .book Page 338 Jeudi, 25. novembre 2004 3:04 15

338

Au cur de Java 2 - Notions fondamentales

Exemple : modification du "look and feel"


Par dfaut, les programmes Swing utilisent laspect et le comportement (look and feel) Metal. Il y a
deux moyens de choisir un look and feel diffrent. Le premier consiste placer, dans les rpertoires
jre/lib, un fichier swing.properties qui donne la proprit swing.defaultlaf de la classe le
look and feel que vous dsirez employer. Par exemple :
swing.defaultlaf=com.sun.java.swing.plaf.motif.MotifLookAndFeel

Notez que le look and feel Metal est situ dans le package javax.swing. Les autres look and feel se
trouvent dans le package com.sun.java et ne doivent pas ncessairement tre prsents dans chaque
implmentation de Java. Actuellement, pour des raisons de copyright, les look and feel Windows et
Mac sont uniquement fournis avec les versions Windows ou Mac de lenvironnement dexcution de
Java.
ASTUCE
Une petite astuce permet de vrifier la prsence des look and feel disponibles. Comme les lignes dbutant par le
caractre # sont ignores dans les fichiers de proprits, vous pouvez spcifier plusieurs look and feel dans le fichier
swing.properties et slectionner celui que vous dsirez en supprimant le caractre #, de la faon suivante :
#swing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel
swing.defaultlaf=com.sun.java.swing.plaf.motif.MotifLookAndFeel
#swing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsLookAndFeel

Vous devez relancer votre programme pour modifier le look and feel de cette manire. Un programme Swing ne lit
quune seule fois le fichier swing.properties, au dmarrage.

La deuxime mthode consiste modifier dynamiquement le look and feel. Appelez la mthode
statique UIManager.setLookAndFeel et passez-lui le nom du look and feel souhait. Appelez
ensuite la mthode statique SwingUtilities.updateComponentTreeUI pour actualiser lensemble
des composants. Vous navez besoin de fournir quun seul composant cette mthode ; elle trouvera
tous les autres. La mthode UIManager.setLookAndFeel peut lancer un certain nombre dexceptions si le look and feel recherch nest pas trouv ou si une erreur survient durant son chargement.
Comme dhabitude, nous vous conseillons de ne pas vous proccuper des exceptions pour linstant ;
elles seront dtailles au Chapitre 11.
Voici un exemple permettant de passer au look and feel Motif :
String plaf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
try
{
UIManager.setLookAndFeel(plaf);
SwingUtilities.updateComponentTreeUI(panel);
}
catch(Exception e) { e.printStackTrace(); }

Pour numrer toutes les implmentations de look and feel installes, appelez
UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();

Vous obtenez ensuite le nom et le nom de classe pour chaque look and feel sous la forme
String name = infos[i].getName();
String className = infos[i].getClassName();

Livre Java .book Page 339 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

339

LExemple 8.2 est un programme de dmonstration qui montre comment changer de look and feel
(voir Figure 8.3). Ce programme ressemble beaucoup celui de lExemple 8.1. Conformment aux
explications de la prcdente section, nous employons une mthode assistante makeButton et une
classe interne anonyme pour spcifier laction du bouton, cest--dire le changement de look and
feel.
Il y a une subtilit dans ce programme. La mthode actionPerformed de la classe couteur daction
interne doit pouvoir passer la rfrence this de la classe outer PlafPanel la mthode updateComponentTreeUI. Souvenez-vous, nous avons vu au Chapitre 6 que le pointeur this de lobjet
outer doit avoir pour prfixe le nom de la classe externe :
SwingUtilities.updateComponentTreeUI( PlafPanel.this);

Figure 8.3
Modification
du look and feel.

Exemple 8.2 : PlafTest.java


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PlafTest
{
public static void main(String[] args)
{
PlafFrame frame = new PlafFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau et des boutons pour
changer le "look and feel"
*/
class PlafFrame extends JFrame

Livre Java .book Page 340 Jeudi, 25. novembre 2004 3:04 15

340

Au cur de Java 2 - Notions fondamentales

{
public PlafFrame()
{
setTitle("PlafTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter un panneau au cadre
PlafPanel panel = new PlafPanel();
add(panel);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
/**
Un panneau avec des boutons pour changer le look and feel
*/
class PlafPanel extends JPanel
{
public PlafPanel()
{
UIManager.LookAndFeelInfo[] infos =
UIManager.getInstalledLookAndFeels();
for (UIManager.LookAndFeelInfo info: infos)
makeButton(info.getName(), info.getClassName());
}
/**
Construit un bouton pour changer le look and feel.
@param name Le nom du bouton
@param plafName Le nom de la classe look and feel
*/
void makeButton(String name, final String plafName)
{
// ajouter un bouton au panneau
JButton button = new JButton(name);
add(button);
// dfinir laction du bouton
button.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// action du bouton: passer au nouveau look and feel
try
{
UIManager.setLookAndFeel(plafName);

Livre Java .book Page 341 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

341

SwingUtilities.updateComponentTreeUI
(PlafPanel.this);
}
catch(Exception e) { e.printStackTrace(); }
}
});
}
}

Exemple : capture des vnements de fentre


Tous les vnements ne sont pas aussi simples grer que les clics de boutons. Voici un exemple
plus complexe que nous avons dj mentionn au Chapitre 7. Avant lapparition de loption EXIT_
ON_CLOSE, dans le JDK 1.3, les programmeurs devaient quitter manuellement le programme lors de
la fermeture du cadre principal. Dans un programme professionnel, vous le ferez galement pour que
le programme ne soit ferm quaprs vous tre assur que lutilisateur ne perdra pas de travail. Par
exemple, vous pouvez souhaiter afficher une bote de dialogue lorsque lutilisateur ferme le cadre,
pour lavertir si un travail non sauvegard risque dtre perdu, et ne sortir quaprs confirmation de
lutilisateur.
Quand lutilisateur tente de fermer un cadre, lobjet JFrame est la source dun vnement
WindowEvent. Nous devons avoir un objet couteur appropri et lajouter la liste des couteurs
de fentre :
WindowListener listener = . . .;
frame.addWindowListener(listener);

Lcouteur de fentre doit tre un objet dune classe implmentant linterface WindowListener, qui
possde sept mthodes. Le cadre les appelle en rponse aux sept vnements distincts qui peuvent se
produire dans une fentre. Voici linterface complte de WindowListener:
public interface WindowListener
{
void windowOpened(WindowEvent e);
void windowClosing(WindowEvent e);
void windowClosed(WindowEvent e);
void windowIconified(WindowEvent e);
void windowDeiconified(WindowEvent e);
void windowActivated(WindowEvent e);
void windowDeactivated(WindowEvent e);
}

INFO
Pour savoir si une fentre a t maximise, vous devez installer un WindowStateListener. Voyez les prochaines
notes API pour en savoir plus.

Comme toujours en Java, toute classe qui implmente une interface doit implmenter toutes les
mthodes de cette interface ; dans ce cas, cela signifie que les sept mthodes doivent tre implmentes.
Une seule nous intresse en loccurrence : la mthode windowClosing.

Livre Java .book Page 342 Jeudi, 25. novembre 2004 3:04 15

342

Au cur de Java 2 - Notions fondamentales

Nous pouvons bien sr dfinir une classe qui implmente linterface, ajouter un appel
System.exit(0) dans la mthode windowClosing et fournir des blocs vides pour les six autres
mthodes :
class Terminator implements WindowListener
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
public
public
public
public
public
public

void
void
void
void
void
void

windowOpened(WindowEvent e) {}
windowClosed(WindowEvent e) {}
windowIconified(WindowEvent e) {}
windowDeiconified(WindowEvent e) {}
windowActivated(WindowEvent e) {}
windowDeactivated(WindowEvent e) {}

Classes adaptateurs
Il est fastidieux dcrire les signatures de six mthodes qui ne font rien. Pour simplifier la tche
du programmeur, chacune des interfaces AWT possdant plusieurs mthodes est accompagne
dune classe adaptateur qui implmente toutes les mthodes de linterface en leur attribuant des
instructions vides. Par exemple, la classe WindowAdapter possde sept mthodes qui ne font
rien. Cela signifie que la classe adaptateur satisfait automatiquement aux exigences techniques
imposes par Java pour limplmentation de linterface couteur qui lui est associe. Nous
pouvons tendre la classe adaptateur afin de spcifier les ractions souhaites pour certains
vnements, mais sans avoir besoin de rpondre explicitement tous les vnements de linterface (une interface comme ActionListener, qui ne possde quune seule mthode, na pas
besoin de classe adaptateur).
Profitons de cette caractristique et utilisons ladaptateur de Window. Nous pouvons tendre la classe
WindowAdapter, hritant ainsi de six mthodes qui ne font rien, et nous contenter de surcharger la
mthode WindowClosing:
class Terminator extends WindowAdapter
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}

Nous pouvons maintenant recenser un objet de type Terminator en tant qucouteur dvnement :
WindowListener listener = new Terminator();
frame.addWindowListener(listener);

Chaque fois que le cadre dclenchera un vnement de fentre, il passera cet vnement lobjet
listener en appelant une de ses sept mthodes (voir Figure 8.4). Six dentre elles ne feront rien ; la
mthode windowClosing appelle System.exit(0) pour terminer lexcution de lapplication.

Livre Java .book Page 343 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

343

ATTENTION
Si vous avez mal orthographi le nom dune mthode lors de lextension dune classe dadaptateur, le compilateur
ninterceptera pas votre erreur. Si vous dfinissez par exemple une mthode windowIsClosing dans une classe
WindowAdapter, vous obtenez une classe avec huit mthodes, et la mthode windowClosing ne fait rien.

Figure 8.4
Un couteur de fentre.

MyFrame

new

Terminator

addWindowListener

windowClosing

windowClosed

La cration dune classe couteur drive de WindowAdapter reprsente une nette amlioration,
mais nous pouvons aller encore plus loin, car il nest pas ncessaire de donner un nom lobjet
listener. Ecrivons simplement :
frame.addWindowListener(new Terminator());

Et pourquoi sarrter en si bon chemin ? Nous pouvons faire de la classe couteur une classe interne
anonyme du cadre :
frame.addWindowListener(new
WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});

Voici les oprations effectues par cet extrait de code :


m

Il dfinit une classe anonyme qui tend la classe WindowAdapter.

Livre Java .book Page 344 Jeudi, 25. novembre 2004 3:04 15

344

Au cur de Java 2 - Notions fondamentales

Il ajoute une mthode windowClosing cette classe anonyme (cette mthode termine lapplication).

Il hrite des six autres mthodes "vides" de WindowAdapter.

Il cre un objet de cette classe. Lobjet est lui-mme anonyme.

Il passe cet objet la mthode addWindowListener.

Comme indiqu, la syntaxe pour lutilisation de classes internes anonymes demande une certaine
habitude. Lintrt est que le code rsultant est aussi court que possible.
java.awt.event.WindowListener 1.1
void windowOpened(WindowEvent e)

Cette mthode est appele lorsque la fentre a t ouverte.

void windowClosing(WindowEvent e)

Cette mthode est appele lorsque lutilisateur a mis une commande de gestionnaire de fentre
pour fermer la fentre. Sachez que la fentre ne se fermera quavec un appel sa mthode hide
ou dispose.

void windowClosed(WindowEvent e)

Cette mthode est appele lorsque la fentre sest ferme.

void windowIconified(WindowEvent e)

Cette mthode est appele une fois que la fentre a t transforme en icne.

void windowDeiconified(WindowEvent e)

Cette mthode est appele lorsque la fentre nest plus ltat dicne.

void windowActivated(WindowEvent e)

Cette mthode est appele lorsque la fentre est devenue active. Seul un cadre ou une bote de
dialogue peut tre actif. Gnralement, le gestionnaire de fentres dcore la fentre active, par
exemple en surlignant sa barre de titre.

void windowDeactivated(WindowEvent e)

Cette mthode est appele lorsque la fentre a t dsactive.


java.awt.event.WindowStateListener 1.4
void windowStateChanged(WindowEvent event)

Cette mthode est appele lorsque la fentre a t maximise, transforme en icne ou restaure
sa taille normale.
java.awt.event.WindowEvent 1.1
int getNewState() 1.4
int getOldState() 1.4

Ces mthodes renvoient lancien tat et le nouvel tat dune fentre dans un vnement de modification de ltat de la fentre. Lentier renvoy correspond lune des valeurs suivantes :
Frame.NORMAL
Frame.ICONIFIED
Frame.MAXIMIZED_HORIZ
Frame.MAXIMIZED_VERT
Frame.MAXIMIZED_BOTH

Livre Java .book Page 345 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

345

Hirarchie des vnements AWT


Aprs vous avoir donn un avant-got de la gestion des vnements, nous allons tudier la gestion
des vnements Java dune manire plus gnrale. Comme nous lavons dj crit, la gestion des
vnements est oriente objet et tous les vnements descendent de la classe EventObject du
package java.util (la superclasse commune ne sappelle pas Event, car ce nom est employ pour
dsigner la classe dvnement dans lancien modle. Bien que celui-ci soit dprci, ses classes
font toujours partie de la bibliothque Java).
La classe EventObject possde une sous-classe AWTEvent qui est le parent de toutes les classes
dvnements AWT. La Figure 8.5 prsente le schma de lhritage des vnements AWT.
Certains composants Swing gnrent des objets vnements qui appartiennent dautres types qui
sont directement drivs dEventObject (et pas dAWTEvent).
Figure 8.5
Event
Object

Schma
de lhritage
des classes
dvnements
AWT.

AWT Event

Action
Event

Adjustment
Event

Component
Event

Item
Event

Focus
Event

Input
Event

Paint
Event

Key
Event

Mouse
Event

MouseWheel
Event

Window
Event

Livre Java .book Page 346 Jeudi, 25. novembre 2004 3:04 15

346

Au cur de Java 2 - Notions fondamentales

Un objet vnement encapsule des informations sur lvnement que la source dvnement
communique aux couteurs. En cas de besoin, nous pouvons alors analyser lobjet vnement qui a
t pass lobjet couteur, comme nous lavons fait laide des mthodes getSource et getActionCommand dans lexemple consacr aux boutons.
Certaines classes dvnements AWT ne sont daucune utilit pratique pour le programmeur Java.
Par exemple, AWT insre des objets PaintEvent dans la file dvnements, mais ces objets ne sont
pas envoys aux couteurs. Les programmeurs Java ncoutent pas les vnements de dessin ; ils
doivent surcharger la mthode paintComponent pour contrler le dessin dun composant. AWT
gnre galement plusieurs vnements qui ne sont ncessaires quaux programmeurs systme, afin
de fournir les systmes de saisie des langues idographiques, les robots de test automatis, etc. Nous
ne traiterons pas de ces types dvnements spcialiss. Nous omettrons enfin les vnements associs
aux composants AWT obsoltes.
Voici une liste des types dvnements AWT souvent utiliss.
ActionEvent
AdjustmentEvent
FocusEvent
ItemEvent

KeyEvent
MouseEvent
MouseWheelEvent
WindowEvent

Nous verrons des exemples dutilisation de ces types dvnements dans ce chapitre et dans le
suivant.
La package javax.swing.event contient dautres vnements spcifiques aux composants Swing.
Nous les tudierons au Chapitre 9.
Les interfaces suivantes permettent dcouter ces vnements. :
ActionListener
AdjustmentListener
FocusListener
ItemListener
KeyListener
Mouse Listener

MouseMotionListener
MouseWheelListener
WindowListener
WindowFocusListener
WindowStateListener

Vous avez dj rencontr dans cet ouvrage les interfaces ActionListener et WindowListener.
Bien que le package javax.swing.event contienne dautres interfaces couteur spcifiques
aux composants Swing, il utilise les couteurs AWT de base pour le traitement gnral des
vnements.
Plusieurs interfaces couteur AWT celles qui possdent plusieurs mthodes sont accompagnes dune classe adaptateur qui implmente toutes les mthodes de linterface afin quelles
naccomplissent aucune action (les autres interfaces nont quune seule mthode et il est donc
inutile demployer des classes adaptateur dans ce cas). Voici les classes adaptateur les plus
souvent utilises :
FocusAdapter
KeyAdapter
MouseAdapter

MouseMotionAdapter
WindowAdapter

Il faut manifestement connatre un grand nombre de classes et dinterfaces ce qui peut paratre
insurmontable premire vue. Heureusement, le principe est simple. Une classe qui dsire recevoir

Livre Java .book Page 347 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

347

des vnements doit implmenter une interface couteur. Elle se recense auprs de la source
dvnement, puis elle reoit les vnements souhaits et les traite grce aux mthodes de linterface
couteur.
INFO C++
Les programmeurs ayant une formation C/C++ se demandent peut-tre pourquoi une telle prolifration dobjets, de
mthodes et dinterfaces est ncessaire la gestion des vnements. Les programmeurs C++ sont habitus
programmer les interfaces utilisateur graphiques laide de callbacks, avec des pointeurs gnriques ou des handles.
Cela ne fonctionnerait pas en Java. Le modle dvnement Java est fortement typ : le compilateur sassure que les
vnements ne sont envoys quaux objets qui sont capables de les grer.

Evnements smantiques et de bas niveau


AWT fait une distinction utile entre vnements de bas niveau et vnements smantiques. Un
vnement smantique exprime ce que fait lutilisateur, par exemple "cliquer sur un bouton" ; en
consquence, un vnement ActionEvent est smantique. Les vnements de bas niveau sont
ceux qui rendent laction possible. Dans le cas dun clic sur un bouton de linterface utilisateur,
cela reprsente une pression sur le bouton de la souris, des dplacements du pointeur de la souris,
puis un relchement du bouton de la souris (mais seulement si le pointeur se trouve encore dans
la zone du bouton affich sur lcran). Ce peut tre galement une pression sur une touche du
clavier, au cas o lutilisateur slectionne le bouton avec la touche de tabulation puis lactive
avec la barre despacement. De la mme manire, un ajustement de la position dune barre de
dfilement est un vnement smantique, mais le dplacement de la souris est un vnement
de bas niveau.
Voici les classes dvnements smantiques les plus utilises dans le package java.awt.event:
m

ActionEvent (pour un clic de bouton, une slection dun lment de menu ou de liste, ou une
pression de la touche Entre dans un champ de texte).

AdjustmentEvent (lutilisateur a dplac le curseur dune barre de dfilement).

ItemEvent (lutilisateur a fait une slection dans un groupe de cases cocher ou dans une liste).

Il y a cinq classes dvnements de bas niveau :


m

KeyEvent (une touche du clavier a t presse ou relche).

MouseEvent (le bouton de la souris a t enfonc ou relch ; le pointeur de la souris a t


dplac ou gliss).

MouseWheelEvent (la molette de la souris a t tourne).

FocusEvent (un composant a obtenu ou perdu le focus). Voyez la section "Evnements de focalisation" dans ce chapitre pour en savoir plus.

WindowEvent (ltat de la fentre a chang).

Livre Java .book Page 348 Jeudi, 25. novembre 2004 3:04 15

348

Au cur de Java 2 - Notions fondamentales

Le Tableau 8.1 montre les interfaces couteur, les vnements et les sources dvnements les plus
importants de AWT.
Tableau 8.1 : Les lments employs pour la gestion des vnements

Interface

Mthodes

Paramtres/accesseurs

Evnements
gnrs par

ActionListener

actionPerformed

ActionEvent

AbstractButton

getActionCommand

JComboBox

getModifiers

JTextField
Timer

AdjustmentListener

adjustmentValueChanged

AdjustmentEvent

JScrollbar

getAdjustable
getAdjustmentType
getValue
ItemListener

itemStateChanged

ItemEvent

AbstractButton

getItem

JComboBox

getItemSelectable
getStateChange
FocusListener

KeyListener

focusGained

FocusEvent

focusLost

isTemporary

keyPressed

KeyEvent

keyReleased

getKeyChar

keyTyped

getKeyCode

Component

Component

getKeyModifiersText
getKeyText
isActionKey
MouseListener

mousePressed

MouseEvent

mouseReleased

getClickCount

mouseEntered

getX

mouseExited

getY

mouseClicked

getPoint
translatePoint

Component

Livre Java .book Page 349 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

349

Tableau 8.1 : Les lments employs pour la gestion des vnements (suite)

Interface

Mthodes

Paramtres/accesseurs

Evnements
gnrs par

MouseMotionListener

mouseDragged

MouseEvent

Component

MouseWheelEvent

Component

mouseMoved
MouseWheelListener

mouseWheelMoved

getWheelRotation
getScrollAmount
WindowListener

windowClosing

WindowEvent

windowOpened

getWindow

Window

windowIconified
windowDeiconified
windowClosed
windowActivated
windowDeactivated
WindowFocusListener

WindowStateListener

windowGainedFocus

WindowEvent

windowLostFocus

getOppositeWindow

windowStateChanged

WindowEvent

Window

Window

getOldState
getNewState

Rsum de la gestion des vnements


Nous revenons au mcanisme de dlgation dvnement afin de nous assurer que vous avez bien
compris les relations existant entre les classes, les interfaces couteurs et les classes adaptateurs.
Les sources dvnement sont des composants de linterface utilisateur, des fentres et des
menus. Le systme dexploitation notifie une source dvnement les activits qui peuvent
lintresser, comme les mouvements de la souris ou les frappes au clavier. La source dvnement dcrit la nature de lvnement dans un objet vnement. Elle stocke galement une liste
dcouteurs des objets qui souhaitent tre prvenus quand lvnement se produit (voir
Figure 8.6). La source dvnement appelle alors la mthode approprie de linterface couteur
afin de fournir des informations sur lvnement aux divers couteurs recenss. Pour cela, la
source passe lobjet vnement adquat la mthode de la classe couteur. Lcouteur analyse
lobjet vnement pour obtenir des informations dtailles. Par exemple, nous pouvons utiliser la
mthode getSource pour connatre la source ou les mthodes getX et getY de la classe MouseEvent pour connatre la position actuelle de la souris.

Livre Java .book Page 350 Jeudi, 25. novembre 2004 3:04 15

350

Au cur de Java 2 - Notions fondamentales

Sachez quil existe des interfaces MouseListener et MouseMotionListener spares, pour des
raisons defficacit. Il se produit de nombreux vnements lorsque lutilisateur dplace la souris. Un
couteur qui sintresse uniquement aux clics de la souris ne doit pas tre inutilement prvenu de
tous les dplacements de la souris.
Figure 8.6
Relation entre sources
dvnement
et couteurs.

Event
source

1 *
<<set of one or more>>

Event
listener

<<implements>>

Listener
interface

Tous les vnements de bas niveau hritent de ComponentEvent. Cette classe dispose dune
mthode, nomme getComponent, qui indique le composant ayant gnr lvnement ; vous
pouvez employer getComponent la place de getSource. La mthode getComponent renvoie la
mme valeur que getSource, mais la dj transtype en Component. Par exemple, si un vnement
clavier a t dclench la suite dune frappe dans un champ de texte, getComponent renvoie une
rfrence ce champ de texte.
java.awt.event.ComponentEvent 1.0

Component getComponent()

Renvoie une rfrence au composant qui est la source de lvnement. Cette mthode correspond
(Component)getSource().

Types dvnements de bas niveau


Dans les sections suivantes, nous allons examiner plus en dtail les vnements qui ne sont pas
lis des composants spcifiques, en particulier les vnements relatifs au clavier et la souris. Le
chapitre suivant abordera les vnements smantiques gnrs par les composants dinterface
utilisateur.

Evnements du clavier
Quand lutilisateur appuie sur une touche du clavier, un vnement KeyEvent avec KEY_PRESSED de
lID est gnr. Sil relche une touche, cela dclenche un vnement KeyEvent avec KEY_
RELEASED. Ces vnements sont intercepts par les mthodes keyPressed et keyReleased de toute
classe qui implmente linterface KeyListener. Utilisez ces mthodes pour intercepter la saisie.
Une troisime mthode, keyTyped, combine les deux mthodes et renvoie les caractres qui ont t
taps.
La meilleure faon de voir ce qui se passe est dutiliser un exemple. Mais, avant cela, nous devons
encore tudier un peu la terminologie. Java tablit une distinction entre les caractres et les codes de

Livre Java .book Page 351 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

351

touche virtuels. Ces derniers sont reprs par un prfixe VK_, comme VK_A ou VK_SHIFT. Les codes
de touche virtuels correspondent aux touches du clavier. Ainsi, VK_A reprsente la touche marque A.
Il ny a pas de code pour les minuscules, car un clavier ne dispose pas de touches spciales pour
celles-ci.
INFO
Les codes de touche virtuels sont comparables aux codes de touche (ou codes scan) du clavier des PC.

Supposons que lutilisateur tape un "A" majuscule de la manire habituelle, en enfonant la touche
Maj (ou Shift) en mme temps que la touche A. Cette manipulation dclenche cinq vnements pour
Java. Voici les actions et les vnements associs :
1. appui sur la touche Maj (keyPressed appele pour VK_SHIFT) ;
2. appui sur la touche A (keyPressed appele pour VK_A) ;
3. frappe de "A" (keyTyped appele pour un "A") ;
4. relchement de la touche A (keyReleased appele pour VK_A) ;
5. relchement de la touche Maj (keyReleased appele pour VK_SHIFT).
En revanche, si lutilisateur tape un "a" minuscule, seuls trois vnements sont dclenchs :
1. appui sur la touche A (keyPressed appele pour VK_A) ;
2. frappe de "a" (keyTyped appele pour un "a") ;
3. relchement de la touche A (keyReleased appele pour VK_A).
Ainsi, la mthode keyTyped indique quel caractre a t tap ("A" ou "a"), alors que keyPressed et
keyReleased indiquent quelle touche a t enfonce par lutilisateur.
Pour travailler avec keyPressed et keyReleased, il faut dabord vrifier le code de touche :
public void keyPressed(KeyEvent event)
{
int keyCode = event.getKeyCode();
. . .
}

Le code de touche quivaut une de ces constantes, dfinies dans la classe KeyEvent:
VK_A . . . VK_Z
VK_0 . . . VK_9
VK_COMMA, VK_PERIOD, VK_SLASH, VK_SEMICOLON, VK_EQUALS
VK_OPEN_BRACKET, VK_BACK_SLASH, VK_CLOSE_BRACKET
VK_BACK_QUOTE, VK_QUOTE
VK_GREATER, VK_LESS, VK_UNDERSCORE, VK_MINUS
VK_AMPERSAND, VK_ASTERISK, VK_AT, VK_BRACELEFT, VK_BRACERIGHT
VK_LEFT_PARENTHESIS, VK_RIGHT_PARENTHESIS
VK_CIRCUMFLEX, VK_COLON, VK_NUMBER_SIGN, VK_QUOTEDBL
VK_EXCLAMATION_MARK, VK_INVERTED_EXCLAMATION_MARK
VK_DEAD_ABOVEDOT, VK_DEAD_ABOVERING, VK_DEAD_ACUTE
VK_DEAD_BREVE
VK_DEAD_CARON, VK_DEAD_CEDILLA, VK_DEAD_CIRCUMFLEX
VK_DEAD_DIAERESIS
VK_DEAD_DOUBLEACUTE, VK_DEAD_GRAVE, VK_DEAD_IOTA, VK_DEAD_MACRON

Livre Java .book Page 352 Jeudi, 25. novembre 2004 3:04 15

352

Au cur de Java 2 - Notions fondamentales

VK_DEAD_OGONEK, VK_DEAD_SEMIVOICED_SOUND, VK_DEAD_TILDE VK_DEAD_VOICED_SOUND


VK_DOLLAR, VK_EURO_SIGN
VK_SPACE, VK_ENTER, VK_BACK_SPACE, VK_TAB, VK_ESCAPE
VK_SHIFT, VK_CONTROL, VK_ALT, VK_ALT_GRAPH, VK_META
VK_NUM_LOCK, VK_SCROLL_LOCK, VK_CAPS_LOCK
VK_PAUSE, VK_PRINTSCREEN
VK_PAGE_UP, VK_PAGE_DOWN, VK_END, VK_HOME, VK_LEFT, VK_UP VK_RIGHT VK_DOWN
VK_F1 . . .VK_F24
VK_NUMPAD0 . . . VK_NUMPAD9
VK_KP_DOWN, VK_KP_LEFT, VK_KP_RIGHT, VK_KP_UP
VK_MULTIPLY, VK_ADD, VK_SEPARATER [sic], VK_SUBTRACT, VK_DECIMAL VK_DIVIDE
VK_DELETE, VK_INSERT
VK_HELP, VK_CANCEL, VK_CLEAR, VK_FINAL
VK_CONVERT, VK_NONCONVERT, VK_ACCEPT, VK_MODECHANGE
VK_AGAIN, VK_ALPHANUMERIC, VK_CODE_INPUT, VK_COMPOSE, VK_PROPS
VK_STOP
VK_ALL_CANDIDATES, VK_PREVIOUS_CANDIDATE
VK_COPY, VK_CUT, VK_PASTE, VK_UNDO
VK_FULL_WIDTH, VK_HALF_WIDTH
VK_HIRAGANA, VK_KATAKANA, VK_ROMAN_CHARACTERS
VK_KANA, VK_KANJI
VK_JAPANESE_HIRAGANA, VK_JAPANESE_KATAKANA, VK_JAPANESE_ROMAN
VK_WINDOWS, VK_CONTEXT_MENU
VK_UNDEFINED

Pour connatre ltat actuel des touches Shift (Maj), Ctrl, Alt et Meta, il est bien sr possible dintercepter les vnements correspondants : VK_SHIFT, VK_CONTROL, VK_ALT et VK_META. Mais cette
technique est ennuyeuse. Il est plus simple dutiliser les mthodes isShiftDown, isControlDown,
isAltDown et isMetaDown (les claviers Sun et Macintosh disposent dune touche META spciale.
Sur un clavier Sun, la touche est signale par un carreau. Sur Macintosh, elle est marque dune
pomme et dune feuille de trfle).
Par exemple, le code qui suit dtermine si lutilisateur a appuy sur les touches Maj et Flche
droite :
public void keyPressed(KeyEvent event)
{
int keyCode = event.getKeyCode();
if (keyCode == keyEvent.VK_RIGHT && event.isShiftDown())
{
. . .
}
}

A lintrieur de la mthode keyTyped, getKeyChar est appele pour obtenir le caractre effectivement
tap.
INFO
Toutes les touches ne dclenchent pas un appel keyTyped. Seules les touches produisant un caractre Unicode
peuvent tre interceptes par la mthode keyTyped. Il faut employer keyPressed pour intercepter les touches
flches et les autres touches de commande.

Livre Java .book Page 353 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

353

LExemple 8.3 montre comment grer la frappe au clavier. Le programme est une simple implmentation du jeu de dessin Etch-A-Sketch, prsent la Figure 8.7.
Figure 8.7
Un petit programme
de dessin.

Vous dplacez un crayon laide des touches flches du clavier. Si vous maintenez simultanment
la touche Maj enfonce, le crayon se dplacera davantage. Au cas o vous seriez habitu lditeur
vi, vous pouvez utiliser respectivement les touches h, j, k, l et H, J, K, L. Les touches flches sont
interceptes avec la mthode keyPressed et les caractres, avec la mthode keyTyped.
Notez une petite caractristique technique. Normalement, un panneau ne peut pas recevoir les
vnements associs aux touches du clavier. Pour supprimer cette limitation, nous appelons la
mthode setFocusable. Nous verrons le concept du focus du clavier plus loin dans ce chapitre.
Exemple 8.3 : Sketch.java
import
import
import
import
import

java.awt.*;
java.awt.geom.*;
java.util.*;
java.awt.event.*;
javax.swing.*;

public class Sketch


{
public static void main(String[] args)
{
SketchFrame frame = new SketchFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau pour dessiner une figure
*/
class SketchFrame extends JFrame
{
public SketchFrame()
{
setTitle("Sketch");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter le panneau au cadre

Livre Java .book Page 354 Jeudi, 25. novembre 2004 3:04 15

354

Au cur de Java 2 - Notions fondamentales

SketchPanel panel = new SketchPanel();


add(panel);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
/**
Un panneau pour dessiner avec le clavier.
*/
class SketchPanel extends JPanel
{
public SketchPanel()
{
last = new Point2D.Double(100, 100);
lines = new ArrayList<Line2D>();
KeyHandler listener = new KeyHandler();
addKeyListener(listener);
setFocusable(true);
}
/**
Ajoute un nouveau segment de ligne au dessin.
@param dx Le mouvement dans la direction x
@param dy Le mouvement dans la direction y
*/
public void add(int dx, int dy)
{
// calculer le nouveau point final
Point2D end = new Point2D.Double(last.getX()+dx,
last.getY()+dy);
// ajouter le segment de ligne
Line2D line = new Line2D.Double(last, end);
lines.add(line);
repaint();
// mmoriser le nouveau point final
last = end;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// dessiner toutes les lignes
for (Line2D l: lines)
g2.draw(l);
}
private Point2D last;
private ArrayList<Line2D> lines;
private static final int SMALL_INCREMENT = 1;
private static final int LARGE_INCREMENT = 5;
private class KeyHandler implements KeyListener

Livre Java .book Page 355 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

355

{
public void keyPressed(KeyEvent event)
{
int keyCode = event.getKeyCode();
// dfinir la distance
int d;
if (event.isShiftDown())
d = LARGE_INCREMENT;
else
d = SMALL_INCREMENT;
// ajouter un segment de ligne
if (keyCode == KeyEvent.VK_LEFT) add(-d, 0);
else if (keyCode == KeyEvent.VK_RIGHT) add(d, 0);
else if (keyCode == KeyEvent.VK_UP) add(0, -d);
else if (keyCode == KeyEvent.VK_DOWN) add(0, d);
}
public void keyReleased(KeyEvent event) {}
public void keyTyped(KeyEvent event)
{
char keyChar = event.getKeyChar();
// dfinir la distance
int d;
if (Character.isUpperCase(keyChar))
{
d = LARGE_INCREMENT;
keyChar = Character.toLowerCase(keyChar);
}
else
d = SMALL_INCREMENT;
// ajouter un segment de ligne
if (keyChar == h) add(-d, 0);
else if (keyChar == l) add(d, 0);
else if (keyChar == k) add(0, -d);
else if (keyChar == j) add(0, d);
}
}
}
java.awt.event.KeyEvent 1.1

char getKeyChar()

Renvoie le caractre tap par lutilisateur.

int getKeyCode()

Renvoie le code de touche virtuel de cet vnement du clavier.

boolean isActionKey()

Renvoie true si la touche de cet vnement est une touche "daction". Les touches daction sont
les suivantes : Origine (Home), Fin (End), Page prcdente (Page up), Page suivante (Page down),
Flche en haut (Up), Flche en bas (Down), Flche gauche (Left), Flche droite (Right),
touches de fonction F1 F24, Impression cran (Print Screen), Arrt dfilement (Scroll Lock),

Livre Java .book Page 356 Jeudi, 25. novembre 2004 3:04 15

356

Au cur de Java 2 - Notions fondamentales

Verrouillage majuscule (Caps Lock), Verrouillage numrique (Num Lock), Pause, Insert,
Suppression (Delete), Entre (Enter), Retour arrire (Backspace) et Touche de tabulation (Tab).

static String getKeyText(int keyCode)

Renvoie une chane dcrivant le code de touche. Par exemple, getKeyText(KeyEvent.VK_END)


renvoie la chane "End" (ou "Fin" si votre version de Java est localise).

static String getKeyModifiersText (int modifiers)

Renvoie une chane dcrivant les touches de modification, comme Maj ou Ctrl+Maj.
Paramtres :

modifiers

Ltat du modificateur indiqu par getModifiers.

java.awt.event.InputEvent 1.1

int getModifiers()

Renvoie un entier dont les bits dcrivent ltat des modificateurs Shift, Control, Alt et Meta.
Cette mthode sapplique la fois aux vnements du clavier et de la souris. Pour savoir si un bit
est dfini, comparez la valeur renvoye avec un des masques binaires SHIFT_MASK, CTRL_MASK,
ALT_MASK, META_GRAPH_MASK, META_MASK ou utilisez une des mthodes suivantes :

boolean isAltDown()
boolean isControlDown()
boolean isMetaDown()
boolean isShiftDown()
boolean isAltGraphDown() 1.2

Ces mthodes renvoient true si la touche de modification correspondante tait enfonce lorsque
lvnement a t gnr.

Evnements de la souris
Il nest pas ncessaire de grer explicitement les vnements de la souris si vous dsirez seulement
que lutilisateur puisse cliquer sur un bouton ou sur un menu. Ces oprations sont gres de faon
interne par les divers composants de linterface utilisateur et traduites en vnements smantiques
appropris. Cependant, si vous voulez permettre lutilisateur de dessiner avec la souris, il vous
faudra intercepter les mouvements, les clics et les oprations de glisser-dplacer de la souris.
Nous allons vous proposer un diteur graphique simplifi qui permettra lutilisateur de placer, de
dplacer et deffacer des carrs sur une grille (voir Figure 8.8).
Figure 8.8
Un programme de test
de la souris.

Livre Java .book Page 357 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

357

Lorsque lutilisateur clique sur un bouton de la souris, trois mthodes de lcouteur sont appeles :
mousePressed quand le bouton est enfonc, mouseReleased quand il est relch et, enfin, mouseClicked. Vous pouvez ignorer les deux premires mthodes si vous ntes intress que par des clics
"complets". En utilisant getX et getY sur largument MouseEvent, vous pouvez obtenir les coordonnes x et y du pointeur de la souris au moment du clic. Si vous souhaitez faire une distinction entre
clic simple et double-clic (voire triple-clic), employez la mthode getClickCount.
Certains concepteurs dinterfaces infligent leurs utilisateurs des combinaisons de clic de souris et
de touches, comme Ctrl+Maj+clic. Cette technique est selon nous assez rprhensible, mais, si cela
vous plat, vous dcouvrirez que linterception des modifications de boutons de souris et de clavier
est trs complique. Dans lAPI dorigine, deux des masques de bouton galent deux masques de
modification de clavier, savoir :
BUTTON2_MASK == ALT_MASK
BUTTON3_MASK == META_MASK

Ceci permet aux utilisateurs qui disposent dune souris un bouton de simuler les autres boutons de
la souris en maintenant enfonces des touches de modification. Toutefois, depuis le JDK 1.4, une
nouvelle approche est recommande. Il existe maintenant des masques :
BUTTON1_DOWN_MASK
BUTTON2_DOWN_MASK
BUTTON3_DOWN_MASK
SHIFT_DOWN_MASK
CTRL_DOWN_MASK
ALT_DOWN_MASK
ALT_GRAPH_DOWN_MASK
META_DOWN_MASK

La mthode getModifiersEx signale prcisment les modificateurs de souris et de clavier dun


vnement de souris.
Notez que, sous Windows, BUTTON3_DOWN_MASK est employ pour dterminer ltat du bouton droit
(non primaire). Voici un exemple permettant de savoir si le bouton droit est enfonc :
if ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK)!=0)
... // code du clic droit

Dans notre programme de dmonstration, nous fournissons la fois une mthode mousePressed et
une mthode mouseClicked. Lorsque vous cliquez sur un pixel qui ne se trouve pas lintrieur
dun des carrs dj dessins, un nouveau carr est ajout dans la fentre. Ce mcanisme est implment dans la mthode mousePressed afin que lutilisateur obtienne une rponse immdiate et nait
pas besoin dattendre le relchement du bouton. Lors dun double-clic lintrieur dun carr existant, celui-ci est effac. Ce traitement du double-clic est implment dans la mthode mouseClicked,
car nous devons connatre le nombre de clics :
public void mousePressed(MouseEvent event)
{
current = find(event.getPoint());
if (current == null) // pas lintrieur dun carr
add(event.getPoint());
}

Livre Java .book Page 358 Jeudi, 25. novembre 2004 3:04 15

358

Au cur de Java 2 - Notions fondamentales

public void mouseClicked(MouseEvent event)


{
current = find(event.getPoint());
if (current!= null && event.getClickCount() >= 2)
remove(current);
}

Quand la souris passe au-dessus dune fentre, celle-ci reoit un flot dvnements de mouvement de souris qui sont ignors par la plupart des applications. Notre programme de dmonstration intercepte malgr tout ces vnements afin de donner au pointeur une forme diffrente (une
croix) lorsquil se trouve au-dessus dun carr. Nous employons pour cela la mthode getPredefinedCursor de la classe Cursor. Le Tableau 8.2 prsente les constantes utilisables avec cette
mthode et les pointeurs tels quils apparaissent sous Windows (notez que plusieurs de ces pointeurs se ressemblent, mais vous devrez vrifier leur vritable aspect sur la plate-forme que vous
utiliserez).
Tableau 8.2 : Exemples de pointeurs Java

Icne

Constante
DEFAULT_CURSOR

CROSSHAIR_CURSOR

HAND_CURSOR

MOVE_CURSOR

TEXT_CURSOR

WAIT_CURSOR

N_RESIZE_CURSOR

Livre Java .book Page 359 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

359

Tableau 8.2 : Exemples de pointeurs Java (suite)

Icne

Constante
NE_RESIZE_CURSOR

E_RESIZE_CURSOR

SE_RESIZE_CURSOR

S_RESIZE_CURSOR

SW_RESIZE_CURSOR

W_RESIZE_CURSOR

NW_RESIZE_CURSOR

ASTUCE
Vous trouverez des images de pointeurs dans le rpertoire jre/lib/images/cursors/. Le fichier
cursors.properties dfinit le pointeur "point chaud" (hot spot). Il sagit du point exact auquel sexcute
laction de la souris. Par exemple, si le pointeur a la forme dune loupe, le point chaud se trouve au centre de la
lentille.

Voici la mthode mouseMoved de MouseMotionListener dans notre exemple de programme :


public void mouseMoved(MouseEvent event)
{
if (find(event.getPoint()) == null)
setCursor(Cursor.getDefaultCursor());
else
setCursor(Cursor.getPredefinedCursor
(Cursor.CROSSHAIR_CURSOR));
}

Livre Java .book Page 360 Jeudi, 25. novembre 2004 3:04 15

360

Au cur de Java 2 - Notions fondamentales

INFO
Vous pouvez dfinir vos propres types de pointeurs grce la mthode createCustomCursor de la classe
Toolkit:
Toolkit tk = Toolkit.getDefaultToolkit();
Image img = tk.getImage("dynamite.gif");
Cursor dynamiteCursor = tk.createCustomCursor(img,
new Point(10, 10), "dynamite stick");

Le premier paramtre de createCustomCursor dtermine limage du pointeur. Le deuxime spcifie le dcalage


de son "point chaud". Le troisime est une chane qui le dcrit. Elle peut tre employe pour le support daccessibilit, par exemple, afin dindiquer la forme du pointeur un utilisateur ayant des problmes de vue ou ne se trouvant
pas directement en face de lcran.

Si lutilisateur appuie sur un bouton de la souris pendant que celle-ci se dplace, des appels
mouseDragged sont gnrs la place des appels mouseMoved. Notre programme de dmonstration permet ainsi de faire glisser un carr se trouvant sous le pointeur. Le carr en cours de dplacement est simplement mis jour pour quil se trouve centr sous la position de la souris. Puis la grille
est redessine pour montrer la nouvelle position de la souris :
public void mouseDragged(MouseEvent event)
{
if (current >= null)
{
int x = event.getX();
int y = event.getY();
current.setFrame(
x - SIDELENGTH / 2,
y - SIDELENGTH / 2,
SIDELENGTH,
SIDELENGTH);
repaint();
}
}

INFO
La mthode mouseMoved nest appele que tant que la souris reste lintrieur du composant. En revanche, la
mthode mouseDragged continue tre appele mme pendant que la souris est tire en dehors du composant.

Deux autres mthodes sont utilises pour la gestion des vnements de souris : mouseEntered et
mouseExited. Elles sont appeles respectivement quand la souris entre dans un composant et quand
elle le quitte.
Nous devons encore expliquer comment couter les vnements de souris. Les clics sont signals par
la mthode mouseClicked, qui fait partie de linterface MouseListener. Comme de nombreuses
applications ne sintressent quaux clics de la souris et pas ses mouvements et comme les
vnements de dplacement se produisent trs frquemment, les vnements de glisser-dplacer de
la souris sont dfinis dans une interface spare appele MouseMotionListener.
Dans notre programme, les deux types dvnements de la souris nous intressent. Deux classes
internes sont dfinies : MouseHandler et MouseMotionHandler. La classe MouseHandler tend la

Livre Java .book Page 361 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

361

classe MouseAdapter, car elle ne dfinit que deux des cinq mthodes de MouseListener. La classe
MouseMotionHandler implmente MouseMotionListener et dfinit les deux mthodes de cette
interface. Le listing du programme est dcrit dans lExemple 8.4.
Exemple 8.4 : MouseTest.java
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.util.*;
java.awt.geom.*;
javax.swing.*;

public class MouseTest


{
public static void main(String[] args)
{
MouseFrame frame = new MouseFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau pour tester
les oprations de la souris
*/
class MouseFrame extends JFrame
{
public MouseFrame()
{
setTitle("MouseTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter un panneau au cadre
MousePanel panel = new MousePanel();
add(panel);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
/**
Un panneau avec des oprations de la souris
pour lajout et la suppression de carrs.
*/
class MousePanel extends JPanel
{
public MousePanel()
{
squares = new ArrayList<Rectangle2D>();
current = null;
addMouseListener(new MouseHandler());
addMouseMotionListener(new MouseMotionHandler());
}

Livre Java .book Page 362 Jeudi, 25. novembre 2004 3:04 15

362

Au cur de Java 2 - Notions fondamentales

public void paintComponent(Graphics g)


{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
// dessiner tous les carrs
for (Rectangle2D r: squares)
g2.draw(r);
}
/**
Trouve le premier carr contenant un point.
@param p Un point
@return Le premier carr contenant p
*/
public Rectangle2D find(Point2D p)
{
for (Rectangle2D r: squares)
{
if (r.contains(p)) return r;
}
return null;
}
/**
Ajoute un carr la collection.
@param p Le centre du carr
*/
public void add(Point2D p)
{
double x = p.getX();
double y = p.getY();
current = new Rectangle2D.Double(
x - SIDELENGTH / 2,
y - SIDELENGTH / 2,
SIDELENGTH,
SIDELENGTH);
squares.add(current);
repaint();
}
/**
Supprime un carr de la collection.
@param s Le carr supprimer
*/
public void remove(Rectangle2D s)
{
if (s == null) return;
if (s == current) current = null;
squares.remove(s);
repaint();
}

Livre Java .book Page 363 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

private static final int SIDELENGTH = 10;


private ArrayList<Rectangle2D> squares;
private Rectangle2D current;
// le carr contenant le pointeur de la souris
private class MouseHandler extends MouseAdapter
{
public void mousePressed(MouseEvent event)
{
/* ajouter un nouveau carr si le pointeur
nest pas lintrieur dun carr */
current = find(event.getPoint());
if (current == null)
add(event.getPoint());
}
public void mouseClicked(MouseEvent event)
{
// supprimer le carr courant si double-clic dessus
current = find(event.getPoint());
if (current!= null && event.getClickCount() >= 2)
remove(current);
}
}
private class MouseMotionHandler
implements MouseMotionListener
{
public void mouseMoved(MouseEvent event)
{
/* dfinit le pointeur sous forme de croix
sil est lintrieur dun carr */
if (find(event.getPoint()) == null)
setCursor(Cursor.getDefaultCursor());
else
setCursor(Cursor.getPredefinedCursor
(Cursor.CROSSHAIR_CURSOR));
}
public void mouseDragged(MouseEvent event)
{
if (current!= null)
{
int x = event.getX();
int y = event.getY();
/* tirer le carr courant pour
le centrer la position (x, y) */
current.setFrame(
x - SIDELENGTH / 2,
y - SIDELENGTH / 2,
SIDELENGTH,
SIDELENGTH);
repaint();
}
}
}
}

363

Livre Java .book Page 364 Jeudi, 25. novembre 2004 3:04 15

364

Au cur de Java 2 - Notions fondamentales

java.awt.event.MouseEvent 1.1

int getX()
int getY()
Point getPoint()

Renvoient les coordonnes x (horizontale) et y (verticale) ou le point auquel sest produit


lvnement dans le systme de coordonnes de la source, partir du coin suprieur gauche du
composant.

void translatePoint(int x, int y)

Dplace les coordonnes de lvnement de x units horizontalement et de y units verticalement.

int getClickCount()

Renvoie le nombre de clics de souris conscutifs associs lvnement (lintervalle qui dtermine
deux clics "conscutifs" dpend du systme).
java.awt.event.InputEvent 1.1

int getModifiersEx() 1.4

Renvoie les modificateurs tendus ou "bas" de cet vnement. Utilisez les valeurs de masque
suivantes pour tester la valeur renvoye :
BUTTON1_DOWN_MASK
BUTTON2_DOWN_MASK
BUTTON3_DOWN_MASK
SHIFT_DOWN_MASK
CTRL_DOWN_MASK
ALT_DOWN_MASK
ALT_GRAPH_DOWN_MASK
META_DOWN_MASK

static String getModifiersExText(int modifiers) 1.4

Renvoie une chane comme "Maj+Button1" dcrivant les modificateurs tendus ou "bas" dans le
jeu de balises donn.
java.awt.Toolkit 1.0

public Cursor createCustomCursor (Image image, Point hotSpot, String name) 1.2

Cre un nouvel objet pointeur personnalis.


Paramtres :

image

Limage afficher lorsque le pointeur est actif.

hotSpot

Le point chaud du pointeur (par exemple lextrmit


dune flche ou le centre dune croix).

name

Une description du pointeur, pour prendre en charge


des options daccessibilit particulires.

java.awt.Component 1.0

public void setCursor(Cursor cursor) 1.1

Attribue au pointeur limage de lun des pointeurs prdfinis spcifis par le paramtre cursor.

Livre Java .book Page 365 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

365

Evnements de focalisation
Lorsque vous utilisez une souris, vous pouvez pointer sur nimporte quel objet lcran. Mais, lorsque vous tapez, les frappes sur les touches doivent concerner un objet spcifique. Le gestionnaire de
fentre (tel que Windows ou X Window) dirige toutes les frappes de touche vers la fentre active.
Souvent, la fentre active se distingue par une barre de titre en surbrillance. Une seule fentre peut
tre active la fois.
Supposons maintenant que la fentre active soit contrle par un programme Java. La fentre Java
reoit les frappes du clavier et les dirige son tour vers un composant particulier. Ce composant est
dsign comme ayant le focus. Tout comme la fentre active se distingue gnralement dune faon
ou dune autre, la plupart des composants Swing fournissent un indice visuel pour indiquer quils
ont le focus. Un champ de texte contient une barre clignotante, un bouton comprend un rectangle
autour du libell, etc. Lorsquun champ de texte a le focus, vous pouvez taper du texte dedans.
Lorsquun bouton a le focus, vous pouvez lactiver en appuyant sur la barre despace du clavier.
Un seul composant dans une fentre peut avoir le focus un moment donn. Un composant peut
perdre le focus si lutilisateur slectionne un autre composant, qui obtient la focalisation. Un
composant obtient le focus lorsque lutilisateur clique dessus avec la souris. Lutilisateur peut
galement employer la touche de tabulation pour faire passer la focalisation dun composant un
autre parmi ceux capables de recevoir la focalisation de saisie. Par dfaut, lordre de focalisation (ou ordre de tabulation) des composants Swing va de la gauche vers la droite et de haut en
bas, selon leur position dans leur conteneur. Cet ordre peut tre modifi, comme nous le verrons
dans le prochain chapitre.
INFO
Malheureusement, dans les versions prcdentes du JDK, la gestion de la focalisation prsentait quelques problmes,
avec plus de 100 bogues signals. Les raisons taient doubles. La focalisation des composants interagit avec la focalisation des fentres, qui revient au systme de fentrage. Certains comportements de focalisation montraient ainsi
des variations dpendantes de la plate-forme. De plus, le code dimplmentation stait apparemment mis hors de
contrle, en particulier avec lajout dune architecture insatisfaisante de tabulation de focalisation dans le JDK 1.3.
Winston Churchill a dclar un jour : "Les Amricains feront toujours le bon choix aprs avoir puis toutes les
alternatives." Apparemment, ceci vaut galement pour lquipe Java. Ils ont enfin fait le bon choix dans le JDK 1.4,
aprs avoir totalement rimplment le code de gestion du focus et fourni une description complte du comportement attendu (avec une explication des dpendances invitables de la plate-forme).
Vous trouverez les spcifications de focalisation ladresse http://java.sun.com/j2se/1.4/docs/api/java/awt/docfiles/FocusSpec.html.

Heureusement, la plupart des programmeurs dapplication nont pas trop sinquiter de la gestion
de la focalisation. Avant le JDK 1.4, il fallait, pour intercepter les vnements de focalisation de
composant, gnralement vrifier les erreurs ou valider les donnes. Supposons que vous ayez un
champ de texte contenant un numro de carte de crdit. Lorsque lutilisateur a termin de modifier le
champ et passe un autre, vous interceptez lvnement de focalisation perdu. Si le format de la
carte de crdit na pas t mis en forme correctement, vous pouvez afficher un message derreur et
redonner la focalisation au champ de la carte de crdit. Toutefois, le JDK 1.4 possde des mcanismes de validation robustes qui sont plus simples programmer. Nous traiterons de la validation au
Chapitre 9.

Livre Java .book Page 366 Jeudi, 25. novembre 2004 3:04 15

366

Au cur de Java 2 - Notions fondamentales

Certains composants, comme les labels ou les panneaux, nobtiennent pas le focus par dfaut car
on suppose quils ne sont utiliss que pour la dcoration ou le groupage. Vous devez craser ce
paramtre par dfaut si vous implmentez un programme de dessin avec des panneaux qui affichent des lments en rponse aux frappes sur le clavier. A partir du JDK 1.4, vous pouvez simplement
appeler :
panel.setFocusable(true);

INFO
Dans les versions plus anciennes du JDK, vous deviez surcharger la mthode isFocusTraversable du composant
pour obtenir le mme effet. Toutefois, lancienne implmentation de focalisation disposait de concepts spars pour
obtenir le focus et raliser la squence de tabulation. Cette distinction entranait des comportements droutants, et
elle a maintenant t supprime. La mthode isFocusTraversable est aujourdhui dprcie.

Dans le reste de cette section, nous verrons des dtails de lvnement de focalisation que vous
pouvez tout fait ignorer, moins que vous nayez un besoin particulier, exigeant un contrle prcis
de la gestion de la focalisation.
Dans le JDK 1.4, vous pouvez facilement retrouver :
m

le propritaire du focus, cest--dire le composant qui a le focus ;

la fentre focalise, la fentre qui contient le propritaire du focus ;

la fentre active, le cadre ou la bote de dialogue qui contient le propritaire du focus.

La fentre focalise est gnralement la mme que la fentre active. Vous nobtiendrez un rsultat
diffrent que lorsque le propritaire du focus est contenu dans une fentre de haut niveau sans dcoration de cadre, comme un menu contextuel.
Pour obtenir cette information, rcuprez tout dabord le gestionnaire de focalisation du clavier :
KeyboardFocusManager manager =
KeyboardFocusManager.getCurrentKeyboardFocusManager();

Puis appelez :
Component owner = manager.getFocusOwner();
Window focused = manager.getFocusedWindow();
Window active = manager.getActiveWindow();
// un cadre ou une bote de dialogue

En ce qui concerne la notification des changements de focalisation, vous devez installer des couteurs de focalisation dans les composants ou les fentres. Un couteur de focalisation de composant
doit implmenter linterface FocusListener avec deux mthodes, focusGained et focusLost. Ces
mthodes sont dclenches lorsque le composant obtient ou perd la focalisation. Chacune de ces
mthodes possde un paramtre FocusEvent. Il existe plusieurs mthodes trs utiles pour cette
classe. La mthode getComponent renvoie le composant qui a reu ou perdu la focalisation, tandis
que la mthode isTemporary renvoie true si le changement de focus na t que temporaire. Un
changement temporaire de focus se produit lorsquun composant perd la focalisation et quil la rcupre
automatiquement. Cest le cas, par exemple, lorsque lutilisateur slectionne une autre fentre active.
Mais, ds quil revient dans la fentre, ce composant retrouve la focalisation.

Livre Java .book Page 367 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

367

Le JDK 1.4 introduit la livraison des vnements de focalisation de fentre. Vous ajoutez un
WindowFocusListener une fentre et implmentez les mthodes windowGainedFocus et
windowLostFocus.
Depuis le JDK 1.4, vous pouvez retrouver le composant ou la fentre "oppos" au moment du
transfert du focus. Lorsquun composant ou une fentre perd le focus, son oppos est le composant ou la fentre qui le rcupre. A linverse, lorsquun composant ou une fentre prend le focus,
son oppos est celui qui la perdu. La mthode getOppositeComponent de la classe FocusEvent
signale le composant oppos, et getOppositeWindow de la classe WindowEvent signale la fentre
oppose.
Avec la programmation, vous pouvez dplacer le focus sur un autre composant en appelant la
mthode requestFocus de la classe Component. Toutefois, le comportement dpend intrinsquement de la plate-forme si le composant nest pas contenu dans la fentre ayant actuellement la
focalisation. Pour permettre aux programmeurs de dvelopper un code indpendant de la plateforme, le JDK 1.4 ajoute la mthode requestFocusInWindow la classe Component. Cette
mthode russit uniquement si le composant est contenu dans la fentre ayant le focus.
INFO
On ne peut pas supposer quun composant a le focus simplement si requestFocus ou requestFocusInWindow
renvoie true. Attendez que lvnement FOCUS_GAINED soit dlivr pour vous en assurer.

INFO
Certains programmeurs sont drouts par lvnement FOCUS_LOST et tentent dempcher un autre composant de
prendre le focus en le rclamant dans le gestionnaire focusLost. Mais, ce moment-l, le focus est dj perdu. Pour
intercepter le focus dans un composant donn, installez un "couteur de changement susceptible de veto" dans
KeyboardFocusManager et placez le veto sur la proprit focusOwner. Consultez le Chapitre 8 du Volume 2 pour
en savoir plus sur le veto des proprits.

java.awt.Component 1.0

void requestFocus()

Demande la focalisation pour ce composant.

boolean requestFocusInWindow() 1.4

Demande la focalisation pour ce composant. Renvoie false si ce composant nest pas contenu
dans la fentre focalise ou si la requte ne peut pas tre traite pour une autre raison. Renvoie
true sil est probable que la requte soit respecte.

void setFocusable(boolean b) 1.4


boolean isFocusable() 1.4

Dfinissent ou rcuprent ltat de ce composant qui peut tre focalis. Si b vaut true, ce
composant peut obtenir le focus.

boolean isFocusOwner() 1.4

Renvoie true si ce composant a actuellement le focus.

Livre Java .book Page 368 Jeudi, 25. novembre 2004 3:04 15

368

Au cur de Java 2 - Notions fondamentales

java.awt.KeyboardFocusManager 1.4

static KeyboardFocusManager getCurrentKeyboardFocusManager()

Rcupre le gestionnaire de focus actuel.

Component getFocusOwner()

Rcupre le composant qui dtient le focus ou null si ce gestionnaire de focus ne gre pas le
composant qui a le focus.

Window getFocusedWindow()

Rcupre la fentre qui contient le composant qui dtient le focus ou null si ce gestionnaire de
focus ne gre pas le composant qui a le focus.

Window getActiveWindow()

Rcupre la bote de dialogue ou le cadre qui contient la fentre focalise ou null si ce gestionnaire de focus ne gre pas la fentre focalise.
java.awt.Window() 1.0

boolean isFocused() 1.4

Renvoie true si cette fentre est la fentre focalise.

boolean isActive() 1.4

Renvoie true si ce cadre ou cette bote de dialogue est la fentre active. Les barres de titre des
cadres et des botes de dialogue actifs sont gnralement marques par le gestionnaire de fentre.
java.awt.event.FocusEvent 1.1

Component getOppositeComponent() 1.4

Renvoie le composant qui a perdu le focus dans le gestionnaire focusGained ou le composant


qui a obtenu le focus dans le gestionnaire focusLost.
java.awt.event.WindowEvent 1.4

Window getOppositeWindow() 1.4

Renvoie la fentre qui a perdu le focus dans le gestionnaire windowGainedFocus, la fentre qui
a obtenu le focus dans le gestionnaire windowLostFocus, la fentre qui a t dsactive dans le
gestionnaire windowActivated ou la fentre qui a t active dans le gestionnaire windowDeactivated.

void windowGainedFocus(WindowEvent event)

Appele lorsque la fentre source de lvnement a obtenu le focus.

void windowLostFocus(WindowEvent event)

Appele lorsque la fentre source de lvnement a perdu le focus.

Actions
Il existe souvent plusieurs moyens dactiver la mme commande. Lutilisateur peut choisir une
mme fonction par lintermdiaire dun menu, dun raccourci clavier ou dun bouton dans une barre
doutils. Cela est facile raliser dans le modle dvnement AWT : vous liez tous les vnements
au mme couteur. Par exemple, supposons que blueAction soit un couteur daction dont la

Livre Java .book Page 369 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

369

mthode actionPerformed change la couleur darrire-plan en bleu. Vous pouvez attacher le mme
objet comme couteur de plusieurs sources dvnement :
m

un bouton de la barre doutils libell "Blue" ;

une option de menu baptise "Blue" ;

une combinaison de touches Crtl+B.

Puis chaque commande de changement de couleur est gre dune seule manire, quelle que soit
laction qui la dclenche : un clic sur un bouton, un choix de menu ou une frappe au clavier.
Le package Swing fournit un mcanisme trs pratique pour encapsuler des commandes et les attacher plusieurs sources dvnement : il sagit de linterface Action. Une action est un objet qui
encapsule :
m

une description de la commande (comme une chane de texte et une icne facultative) ;

les paramtres ncessaires lexcution de la commande (dans notre cas, la couleur dsire).

Une interface Action possde les mthodes suivantes :


void actionPerformed(ActionEvent event)
void setEnabled(boolean b)
boolean isEnabled()
void putValue(String key, Object value)
Object getValue(String key)
void addPropertyChangeListener(PropertyChangeListener listener)
void removePropertyChangeListener(PropertyChangeListener listener)

La premire mthode est la mthode habituelle de linterface ActionListener: en fait, linterface


Action est drive de ActionListener. Par consquent, il est possible dutiliser un objet Action
partout o un objet ActionListener est attendu.
Les deux mthodes suivantes permettent dactiver ou de dsactiver laction, et de vrifier si elle est
active. Lorsquune action attache un menu ou une barre doutils est dsactive, loption correspondante apparat en gris.
Les mthodes putValue et getValue sont employes pour stocker et rcuprer un couple arbitraire
nom/valeur dans lobjet Action. Il existe deux chanes prdfinies Action.NAME et
Action.SMALL_ICON pour faciliter le stockage des noms et des icnes dans un objet Action:
action.putValue(Action.NAME, "Blue");
action.putValue(Action.SMALL_ICON,
new ImageIcon("blue-ball.gif"));

Le Tableau 8.3 dcrit les noms des actions prdfinies.


Tableau 8.3 : Noms des actions prdfinies

Nom

Valeur

NAME

Nom de laction ; affich sur les boutons et les options de menu.

SMALL_ICON

Emplacement de stockage dune petite icne ; pour affichage sur un bouton,


une option de menu ou dans la barre doutils.

SHORT_DESCRIPTION

Courte description de licne ; pour affichage dans une bulle daide.

Livre Java .book Page 370 Jeudi, 25. novembre 2004 3:04 15

370

Au cur de Java 2 - Notions fondamentales

Tableau 8.3 : Noms des actions prdfinies (suite)

Nom

Valeur

LONG_DESCRIPTION

Description dtaille de licne ; pour utilisation potentielle dans laide en


ligne. Aucun composant Swing nutilise cette valeur.

MNEMONIC_KEY

Abrviation mnmonique ; pour affichage dans une option de menu (voir


Chapitre 9).

ACCELERATOR_KEY

Emplacement pour le stockage dun raccourci clavier. Aucun composant


Swing nutilise cette valeur.

ACTION_COMMAND_KEY

Utilise dans la mthode registerKeyboardAction, maintenant obsolte.

DEFAULT

Proprit fourre-tout. Aucun composant Swing nutilise cette valeur.

Si lobjet Action est ajout un menu ou une barre doutils, le nom et licne sont automatiquement rcuprs et affichs dans loption du menu ou sur le bouton de la barre doutils. La valeur de
SHORT_DESCRIPTION saffiche dans une bulle daide.
Les deux dernires mthodes de linterface Action permettent aux autres objets en particulier les menus ou barres doutils qui ont dclench laction de recevoir une notification
lorsque les proprits de lobjet Action sont modifies. Par exemple, si un menu est recens en
tant qucouteur de changement de proprit dun objet Action, et que lobjet Action soit
ensuite dsactiv, le menu est prvenu et peut alors afficher en gris la rubrique correspondant
laction. Les couteurs de changement de proprit sont une construction gnrique intgre
au modle de composant des Java beans. Vous en saurez plus sur les beans et leurs proprits au
Volume 2.
Notez que Action est une interface et non une classe. Toute classe implmentant cette interface doit
donc implmenter les sept mthodes cites. Heureusement, un bon samaritain a implment toutes
ces mthodes sauf actionPerformed dans une classe nomme AbstractAction, qui se
charge de stocker les couples nom/valeur et de grer les couteurs de changement de proprit. Il ne
vous reste plus qu tendre AbstractAction et crire une mthode actionPerformed.
Nous allons construire un objet Action capable dexcuter la commande de changement de couleur.
Nous allons lui fournir le nom de la commande, une icne et la couleur souhaite. Nous allons stocker la couleur dans la table des couples nom/valeur propose par la classe AbstractAction. Voici le
code de la classe ColorAction. Le constructeur spcifie le couple nom/valeur et la mthode
actionPerformed se charge de modifier la couleur :
public class ColorAction extends AbstractAction
{
public ColorAction(String name, Icon icon, Color c)
{
putValue(Action.NAME, name);
putValue(Action.SMALL_ICON, icon);
putValue("color", c);
putValue(Action.SHORT_DESCRIPTION, "Set panel color to "
+ name.toLowerCase());
}

Livre Java .book Page 371 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

371

public void actionPerformed(ActionEvent event)


{
Color c = (Color)getValue("color");
setBackground(c);
}
}

Notre programme de test cre trois objets de cette classe, comme celui-ci :
Action blueAction = new ColorAction("Blue",
new ImageIcon("blue-ball.gif"),Color.BLUE);

Associons maintenant cette action un bouton. Cest une opration aise, car il existe un constructeur de
JButton qui accepte un objet Action:
JButton blueButton = new JButton(blueAction);

Ce constructeur lit le nom et licne dans laction, affecte la courte description la bulle daide et
dfinit laction en tant qucouteur. Vous pouvez voir licne et la bulle daide la Figure 8.9.
Vous verrez dans le prochain chapitre quil est aussi trs simple dajouter la mme action un menu.
Figure 8.9
Les boutons affichent les
icnes provenant des
objets Action.

Il nous reste associer les objets Action au clavier pour que les actions soient ralises lorsque
lutilisateur tape des commandes au clavier. Nous nous trouvons alors devant une certaine
complexit technique. Les frappes de touches sont notifies au composant qui dtient le focus, mais
notre application est constitue de plusieurs composants : trois boutons dans un panneau. A tout
moment, nimporte lequel des trois boutons peut dtenir le focus. Cela signifie que chacun des trois
boutons doit grer les vnements du clavier et tre lcoute des combinaisons de touches Ctrl+Y,
Ctrl+B et Ctrl+R.
Ce problme est courant et les concepteurs de Swing ont imagin une solution pratique pour le
rsoudre.
INFO
En fait, dans la version 1.2 de JDK, il existait deux solutions diffrentes pour lier les touches aux actions : la mthode
registerKeyboardAction de la classe JComponent et le concept KeyMap pour les commandes JTextComponent. En ce qui concerne le JDK version 1.3, ces deux mcanismes sont unifis. Cette section dcrit lapproche
commune.

Pour associer les actions des frappes de touches, nous devons dabord crer des objets de la classe
KeyStroke. Il sagit dune classe trs pratique qui encapsule la description dune touche. Pour gnrer un objet KeyStroke, nous nappelons pas un constructeur, mais nous employons la mthode

Livre Java .book Page 372 Jeudi, 25. novembre 2004 3:04 15

372

Au cur de Java 2 - Notions fondamentales

statique getKeyStroke de la classe KeyStroke. Nous lui spcifions le code de touche virtuel et les
indicateurs (comme les combinaisons de touches Maj et Ctrl) :
KeyStroke ctrlBKey = KeyStroke.getKeyStroke(KeyEvent.VK_B,
InputEvent.CTRL_MASK);

Il existe une mthode pratique qui vous permet de dfinir la combinaison de touches sous forme de
chane :
KeyStroke ctrlBKey = KeyStroke.getKeyStroke("ctrl B");

Chaque JComponent a trois affectations dentre qui associent les objets KeyStroke aux actions.
Les trois affectations correspondent trois conditions diffrentes (voir Tableau 8.4).
Tableau 8.4 : Conditions daffectations dentre

Indicateur

Invoquer laction

WHEN_FOCUSED

Quand ce composant a le focus clavier.

WHEN_ANCESTOR_OF_FOCUSED_
COMPONENT

Quand ce composant contient le composant qui a le focus clavier.

WHEN_IN_FOCUSED_WINDOW

Quand ce composant se trouve dans la mme fentre que le


composant qui a le focus clavier.

Le traitement des frappes au clavier vrifie ces affectations dans lordre suivant :
1. Vrifie lindicateur WHEN_FOCUSED du composant ayant le focus de saisie. Si la combinaison de
touches existe, excute laction correspondante. Si laction est active, stoppe le traitement.
2. En commenant par le composant ayant le focus de saisie, vrifie les indicateurs WHEN_
ANCESTOR_OF_FOCUSED_COMPONENT de ses composants parents. Ds quune correspondance
avec la combinaison de touches est trouve, excute laction correspondante. Si laction est active, stoppe le traitement.
3. Recherche tous les composants visibles et activs dans la fentre avec le focus dentre, ayant
cette combinaison de touches enregistre dans un indicateur WHEN_IN_FOCUSED_WINDOW. Donne
ces composants (dans lordre de leur enregistrement) une chance dexcuter laction correspondante. Ds que la premire action active est excute, stoppe le traitement. Cette partie du
processus est toutefois peu fiable, si une combinaison de touches apparat dans plus dun indicateur
WHEN_IN_FOCUSED_WINDOW.
Vous obtenez une affectation dentre du composant laide de la mthode getInputMap, par
exemple :
InputMap imap = panel.getInputMap(JComponent.WHEN_FOCUSED);

La condition WHEN_FOCUSED signifie que cette affectation est consulte lorsque le composant courant
a le focus clavier. Dans notre cas, ce nest pas laffectation que nous voulons. Lun des boutons, et
non le panneau, a le focus de saisie. Lun des deux autres choix daffectation fonctionne trs bien
pour linsertion de la combinaison de touches relative au changement de couleur. Dans notre exemple
de programme, nous utilisons WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.

Livre Java .book Page 373 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

373

InputMap naffecte pas directement les objets KeyStroke aux objets Action. Laffectation est faite
vers des objets arbitraires, et une seconde affectation, implmente par la classe ActionMap, affecte
les objets aux actions. Cela facilite le partage des mmes actions parmi les combinaisons de touches
provenant de diffrentes affectations dentre.
Chaque composant a donc trois affectations dentre et une affectation daction. Pour les associer, vous devez proposer des noms pour les actions. Voici comment attacher une touche une
action :
imap.put(KeyStroke.getKeyStroke("ctrl Y"), "panel.yellow");
ActionMap amap = panel.getActionMap();
amap.put("panel.yellow", yellowAction);

Il est courant demployer la chane "none" pour une action qui ne fait rien. Il est ainsi facile de
dsactiver une touche :
imap.put(KeyStroke.getKeyStroke("ctrl C"), "none");

ATTENTION
La documentation JDK suggre dutiliser le nom de laction comme cl de recherche daction. Ce nest pas forcment
une bonne ide. Le nom de laction est affich sur les boutons et dans les options de menu ; il peut par consquent
changer selon lhumeur du concepteur de linterface utilisateur ou tre traduit en cas de localisation du programme.
De telles chanes, instables, ne sont pas de bons choix pour une recherche. Nous vous recommandons donc de choisir,
pour les actions, des noms indpendants de ceux affichs.

Pour rsumer, voici comment procder pour raliser la mme action en rponse un clic de bouton,
un choix de menu ou une frappe de touches :
1. Crez une classe qui tend la classe AbstractAction. Vous devez pouvoir utiliser la mme
classe pour plusieurs actions connexes.
2. Crez un objet de la classe action.
3. Crez un bouton ou une option de menu partir de lobjet action. Le constructeur lira le texte de
libell et licne dans lobjet action.
4. Pour les actions pouvant tre dclenches par des frappes de touches, vous devez raliser des
tapes supplmentaires. Localisez dabord le composant de plus haut niveau dans la fentre, tel
quun panneau qui contient tous les autres composants.
5. Extrayez laffectation dentre WHEN_ANCESTOR_OF_FOCUSED_COMPONENT de ce composant de
haut niveau. Crez un objet KeyStroke pour la combinaison de touches dsire. Crez un objet
touche daction, tel quune chane qui dcrit votre action. Ajoutez la paire (frappe de touches,
touche daction) dans laffectation dentre.
6. Enfin, extrayez laffectation dentre du composant de plus haut niveau. Ajoutez la paire (touche
daction, objet action) laffectation.
LExemple 8.5 montre le code complet du programme qui affecte la fois les boutons et les frappes
de touches aux objets action. Testez-le le fait de cliquer sur les boutons ou de taper Ctrl+Y,
Ctrl+B ou Ctrl+R, modifie la couleur du panneau.

Livre Java .book Page 374 Jeudi, 25. novembre 2004 3:04 15

374

Au cur de Java 2 - Notions fondamentales

Exemple 8.5 : ActionTest.java


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ActionTest
{
public static void main(String[] args)
{
ActionFrame frame = new ActionFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau pour faire la dmonstration
des actions de changement de couleur.
*/
class ActionFrame extends JFrame
{
public ActionFrame()
{
setTitle("ActionTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter un panneau au cadre
ActionPanel panel = new ActionPanel();
add(panel);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
/**
Un panneau avec des boutons et des raccourcis clavier
pour modifier la couleur darrire-plan.
*/
class ActionPanel extends JPanel
{
public ActionPanel()
{
// dfinir les actions
Action yellowAction = new ColorAction("Yellow",
new ImageIcon("yellow-ball.gif"),
Color.YELLOW);
Action blueAction = new ColorAction("Blue",
new ImageIcon("blue-ball.gif"),
Color.BLUE);

Livre Java .book Page 375 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

Action redAction = new ColorAction("Red",


new ImageIcon("red-ball.gif"),
Color.RED);
// ajouter les boutons pour ces actions
add(new JButton(yellowAction));
add(new JButton(blueAction));
add(new JButton(redAction));
// associer des noms aux touches Y, B et R
InputMap imap = getInputMap(
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
imap.put(KeyStroke.getKeyStroke("ctrl Y"), "panel.yellow");
imap.put(KeyStroke.getKeyStroke("ctrl B"), "panel.blue");
imap.put(KeyStroke.getKeyStroke("ctrl R"), "panel.red");
// associer les noms aux actions
ActionMap amap = getActionMap();
amap.put("panel.yellow", yellowAction);
amap.put("panel.blue", blueAction);
amap.put("panel.red", redAction);
}
public class ColorAction extends AbstractAction
{
/**
Construit une action couleur.
@param name Le nom du bouton
@param icon Licne du bouton
@param c La couleur darrire-plan
*/
public ColorAction(String name, Icon icon, Color c)
{
putValue(Action.NAME, name);
putValue(Action.SMALL_ICON, icon);
putValue(Action.SHORT_DESCRIPTION,
"Set panel color to "+name.toLowerCase());
putValue("color", c);
}
public void actionPerformed(ActionEvent event)
{
Color c = (Color)getValue("color");
setBackground(c);
repaint();
}
}
}

375

Livre Java .book Page 376 Jeudi, 25. novembre 2004 3:04 15

376

Au cur de Java 2 - Notions fondamentales

java.swing.Action 1.2

void setEnabled(boolean b)

Active ou dsactive cette action.

boolean isEnabled()

Renvoie true si laction est active.

void putValue(String key, Object value)

Place une paire nom/valeur dans lobjet Action.


Paramtres :

key

Le nom de la fonctionnalit stocker avec lobjet action.


Cela peut tre nimporte quelle chane, mais il en existe
quatre prdfinies (voir Tableau 8.3).

value

Lobjet associ au nom.

Object getValue(String key)

Renvoie la valeur extraite dun couple nom/valeur.


javax.swing.JMenu 1.2

JMenuItem add(Action a)

Ajoute au menu un lment (une option) qui invoque laction a lorsquil est slectionn ; renvoie
llment de menu ajout.
javax.swing.KeyStroke 1.2

static KeyStroke getKeyStroke(char keyChar)

Cre un objet KeyStroke qui encapsule une frappe de touche correspondant un vnement
KEY_TYPED.

static KeyStroke getKeyStroke(int keyCode, int modifiers)


static KeyStroke getKeyStroke(int keyCode, int modifiers, boolean onRelease)

Crent un objet KeyStroke qui encapsule une frappe de touche correspondant un vnement
KEY_PRESSED ou KEY_RELEASED (touche appuye ou relche).
Paramtres :

keyCode

La valeur de la touche virtuelle.

modifiers

Toute combinaison de InputEvent.SHIFT_MASK,


InputEvent.CTRL_MASK, InputEvent.ALT_MASK,
InputEvent.META_MASK

onRelease vaut true si la frappe de touches doit tre reconnue lorsque la touche est relche.

static KeyStroke getKeyStroke(String description)

Cre un objet KeyStroke partir dune description en clair. La description est une squence de
mots cls spars par des espaces, au format suivant :
1. Les mots cls correspondant shift control ctrl meta alt button1 button2 button3
sont traduits en masque binaire appropri.
2. La chane typed doit tre suivi dune chane dun seul caractre, par exemple, "typed a".

Livre Java .book Page 377 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

377

3. Un mot cl pressed ou released indique une touche appuye ou relche. (Appuye est la
valeur par dfaut).
4. Sinon la chane, lorsquelle est prfixe par VK_, doit correspondre une constante KeyEvent;
ainsi, "INSERT" correspond KeyEvent.VK_INSERT.
Par exemple, "released ctrl Y" correspond :
getKeyStroke(KeyEvent.VK_Y, Event.CTRL_MASK, true)
javax.swing.JComponent 1.2

ActionMap getActionMap() 1.3

Renvoie laffectation daction qui affecte les frappes de touches aux touches daction.

InputMap getInputMap(int flag) 1.3

Extrait laffectation dentre qui affecte les touches daction aux objets action.
Paramtres :

flag

Une condition du focus de saisie pour dclencher laction,


dont les valeurs figurent au Tableau 8.4.

Multidiffusion
Dans la section prcdente, plusieurs sources dvnement notifiaient le mme couteur dvnement. Nous allons maintenant tudier le mcanisme inverse. Toutes les sources dvnement AWT
prennent en charge un modle de multidiffusion (multicasting) pour les couteurs. Cela signifie
que le mme vnement peut tre envoy plusieurs objets couteurs. La multidiffusion se rvle
utile si un vnement est potentiellement intressant pour plusieurs couteurs. Il suffit dajouter
plusieurs couteurs une source dvnement pour donner chacun deux une chance de ragir
aux vnements.
ATTENTION
Selon la documentation JDK, "LAPI ne garantit pas lordre dans lequel les vnements sont diffuss plusieurs couteurs recenss auprs dune mme source". En particulier, nimplmentez pas une logique qui dpend de lordre de
livraison.

Nous allons prsenter une petite application utilisant la multidiffusion. Nous allons crer un cadre
capable de gnrer dautres fentres laide dun bouton "New" et de toutes les fermer grce un
bouton "Close all" (voir Figure 8.10).
Lcouteur du bouton New de MulticastPanel est lobjet newListener construit dans le constructeur
de MulticastPanel il construit un nouveau cadre chaque fois que le bouton est cliqu.
Mais le bouton Close all de MulticastPanel a plusieurs couteurs. A chaque excution du
constructeur BlankFrame, il ajoute un autre couteur daction au bouton Close all. Chacun de ces
couteurs est responsable de la fermeture dun unique cadre dans sa mthode actionPerformed.
Lorsque lutilisateur clique sur le bouton Close all, chacun des couteurs est activ, et chacun deux
ferme son cadre.

Livre Java .book Page 378 Jeudi, 25. novembre 2004 3:04 15

378

Au cur de Java 2 - Notions fondamentales

En outre, la mthode actionPerformed supprime lcouteur du bouton Close all car il nest plus
ncessaire une fois le cadre ferm.
Figure 8.10
Toutes les fentres sont
lcoute de la commande
Tout fermer.

LExemple 8.6 contient le code source de lapplication.


Exemple 8.6 : MulticastTest.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MulticastTest
{
public static void main(String[] args)
{
MulticastFrame frame = new MulticastFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec des boutons pour crer et fermer
des cadres secondaires
*/
class MulticastFrame extends JFrame
{
public MulticastFrame()

Livre Java .book Page 379 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

{
setTitle("MulticastTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter un panneau au cadre
MulticastPanel panel = new MulticastPanel();
add(panel);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
/**
Un panneau avec des boutons pour crer et fermer des cadres.
*/
class MulticastPanel extends JPanel
{
public MulticastPanel()
{
// ajouter un bouton "New"
JButton newButton = new JButton("New");
add(newButton);
final JButton closeAllButton = new JButton("Close all");
add(CloseAllButton);
ActionListener newListener = new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
BlankFrame frame = new BlankFrame(closeAllButton);
frame.setVisible(true);
}
};
newButton.addActionListener(newListener);
}
}
/**
Un cadre vide qui peut tre ferm par un clic sur un bouton.
*/
class BlankFrame extends JFrame
{
/**
Construit un cadre vide
@param closeButton Le bouton pour fermer ce cadre
*/
public BlankFrame(final JButton closeButton)
{
counter++;
setTitle("Frame "+counter);
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
setLocation(SPACING * counter, SPACING * counter);

379

Livre Java .book Page 380 Jeudi, 25. novembre 2004 3:04 15

380

Au cur de Java 2 - Notions fondamentales

closeListener = new
ActionListener()
{
closeButton.removeActionListener(closeListener);
dispose();
}
};
closeButton.addActionListener(closeListener);
}
Private ActionListener closeListener;
public static final int DEFAULT_WIDTH = 200;
public static final int DEFAULT_HEIGHT = 150;
public static final int SPACING = 40;
private static int counter = 0;
}

Implmenter des sources dvnements


Dans la dernire section de ce chapitre, nous verrons comment implmenter une classe qui gnre
ses propres vnements et notifie les couteurs intresss. Ceci est parfois ncessaire lorsque vous
utilisez des composants Swing avancs. Il est aussi intressant de voir ce qui se passe en coulisse
lorsque vous ajoutez un couteur un composant.
Notre source dvnement sera PaintCountPanel, qui compte la frquence dappel de la mthode
paintComponent. A chaque fois que le compte est incrment, PaintCountPanel avertit tous les
couteurs. Dans notre exemple de programme, nous attacherons un seul couteur qui actualise le
titre du cadre (voir Figure 8.11).
Lorsque vous dfinissez une source dvnement, trois ingrdients sont ncessaires :
m

Un type dvnement. Nous pourrions dfinir notre propre classe dvnement, mais nous utilisons
simplement la classe PropertyChangeEvent existante.

Une interface dcouteur dvnement. Une fois de plus, nous pourrions dfinir notre propre
interface, mais nous utiliserons linterface PropertyChangeListener existante. Cette interface
possde une seule mthode :
public void propertyChange(PropertyChangeEvent event)

Des mthodes pour ajouter et supprimer des couteurs. Nous fournirons deux mthodes dans
la classe PaintCountPanel:
public void addPropertyChangeListener(PropertyChangeListener listener)
public void removePropertyChangeListener(PropertyChangeListener listener)

Comment sassurer que les vnements sont envoys aux parties intresses ? Cest l la responsabilit de la source de lvnement. Elle doit construire un objet dvnement et le passer aux couteurs
recenss ds quun vnement survient.
La gestion des vnements est une tche commune, et les concepteurs de Swing fournissent une
classe commode, intitule EventListenerList, pour faciliter limplmentation des mthodes et
ainsi ajouter et supprimer des couteurs et dclencher des vnements. La classe soccupe des
dtails gnants qui peuvent survenir lorsque plusieurs threads tentent dajouter, de supprimer ou
de distribuer simultanment des vnements.

Livre Java .book Page 381 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

381

Certaines sources dvnements acceptant les couteurs de plusieurs types, chaque couteur de la
liste des couteurs dvnements est associ une classe particulire. Les mthodes add et remove
sont destines limplmentation des mthodes addXxxListener. Par exemple :
public void addPropertyChangeListener(PropertyChangeListener listener)
{
listenerList.add(PropertyChangeListener.class, listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener)
{
listenerList.remove(PropertyChangeListener.class, listener);
}

INFO
Vous vous demandez peut-tre pourquoi EventListenerList ne vrifie pas simplement linterface implmente
par lobjet couteur. Or un objet peut implmenter plusieurs interfaces. Il est possible, par exemple, que listener
implmente la fois PropertyChangeListener et linterface ActionListener, mais un programmeur peut choisir de ne lajouter que sous forme de PropertyChangeListener en appelant addPropertyChangeListener.
EventListenerList doit respecter ce choix.

Ds que la mthode paintComponent est appele, la classe PaintCountPanel construit un objet


PropertyChangeEvent indiquant la source de lvnement, le nom de la proprit et lancienne et la
nouvelle valeur de la proprit. Elle appelle ensuite la mthode daide firePropertyChangeEvent:
public void paintComponent(Graphics g)
{
int oldPaintCount = paintCount;
paintCount++;
firePropertyChangeEvent(new PropertyChangeEvent(this,
"paintCount", oldPaintCount, paintCount));
super.paintComponent(g);
}

La mthode firePropertyChangeEvent localise tous les couteurs recenss et appelle leurs mthodes
propertyChange:
public void firePropertyChangeEvent(PropertyChangeEvent event)
{
EventListener[] listeners =
listenerList.getListeners(PropertyChangeListener.class);
for (EventListener l: listeners)
((PropertyChangeListener) l).propertyChange(event);
}

LExemple 8.7 montre le code source du programme dexemple qui coute un PaintCountPanel.
Le constructeur du cadre ajoute un couteur de changement de proprit qui actualise le titre du cadre :
panel.addPropertyChangeListener(new
PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent event)
{
setTitle("EventSourceTest - " + event.getNewValue());
}
});

Livre Java .book Page 382 Jeudi, 25. novembre 2004 3:04 15

382

Au cur de Java 2 - Notions fondamentales

Ceci termine notre discussion sur la gestion des vnements. Dans le prochain chapitre, vous en
saurez plus sur les composants de linterface utilisateur. Bien entendu, pour programmer des interfaces utilisateur, vous exploiterez vos connaissances de la gestion dvnements, en capturant les
vnements gnrs par les composants de linterface utilisateur.
Figure 8.11
Comptage de la frquence
de dessin du panneau.

Exemple 8.7 : EventSourceTest.java


import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;
java.beans.*;

public class EventSourceTest


{
public static void main(String[] args)
{
EventSourceFrame frame = new EventSourceFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre qui contient un panneau avec des dessins
*/
class EventSourceFrame extends JFrame
{
public EventSourceFrame()
{
setTitle("EventSourceTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter le panneau au cadre
final PaintCountPanel panel = new PaintCountPanel();
add(panel);
panel.addPropertyChangeListener(new
PropertyChangeListener()
{

Livre Java .book Page 383 Jeudi, 25. novembre 2004 3:04 15

Chapitre 8

Gestion des vnements

public void propertyChange(PropertyChangeEvent event)


{
setTitle("EventSourceTest - " + event.getNewValue());
}
});
}
public static final int DEFAULT_WIDTH = 400;
public static final int DEFAULT_HEIGHT = 200;
}
/**
Un panneau qui compte la frquence du dessin.
*/
class PaintCountPanel extends JPanel
{
public void paintComponent(Graphics g)
{
int oldPaintCount = paintCount;
paintCount++;
firePropertyChangeEvent(new PropertyChangeEvent(this,
"paintCount", oldPaintCount, paintCount));
super.paintComponent(g);
}
/**
Ajoute un couteur de changement
@param listener Lcouteur ajouter
*/
public void addPropertyChangeListener(PropertyChangeListener listener)
{
listenerList.add(PropertyChangeListener.class, listener);
}
/**
Supprime un couteur de changement
@param listener Lcouteur supprimer
*/
public void removePropertyChangeListener(PropertyChangeListener listener)
{
listenerList.remove(PropertyChangeListener.class, listener);
}
public void firePropertyChangeEvent(PropertyChangeEvent event)
{
EventListener[] listeners =
listenerList.getListeners(PropertyChangeListener.class);
for (EventListener l: listeners)
((PropertyChangeListener) l).propertyChange(event);
}
public int getPaintCount()
{
return paintCount;
}
private int paintCount;
}

383

Livre Java .book Page 384 Jeudi, 25. novembre 2004 3:04 15

384

Au cur de Java 2 - Notions fondamentales

javax.swing.event.EventListenerList 1.2

void add(Class t, EventListener l)

Ajoute un couteur dvnement et sa classe la liste. La classe est stocke de sorte que les
mthodes de dclenchement dvnements puissent appeler chacun des vnements. Une utilisation
ordinaire se trouve dans une mthode addXxxListener:
public void addXxxListener(XxxListener l)
{
listenerList.add(XxxListener.class, l);
}

Paramtres

Le type dcouteur.

Lcouteur.

void remove(Class t, EventListener l)

Supprime un couteur dvnement et sa classe de la liste. Une utilisation gnrale se trouve


dans une mthode removeXxxListener:
public void removeXxxListener(XxxListener l)
{
listenerList.remove(XxxListener.class, l);
}

Paramtres

Le type dcouteur.

Lcouteur.

EventListener[] getListeners(Class t) 1.3

Renvoie un tableau de tous les couteurs du type donn. Le tableau est garanti tre non null.

Object[] getListenerList()

Renvoie un tableau dont les lments ayant un indice pair sont des classes dcouteurs et dont les
lments ayant un indice impair sont des objets couteur. Le tableau est garanti comme tant non
null.
java.beans.PropertyChangeEvent 1.1

PropertyChangeEvent(Object source, String name, Object oldValue, Object newValue)

Construit un vnement de changement de proprit.


Paramtres

source

La source de lvnement, cest--dire lobjet qui signale


un changement de proprit.

name

Le nom de la proprit.

oldValue

La valeur de la proprit avant le changement.

newValue

La valeur de la proprit aprs le changement.

java.beans.PropertyChangeListener 1.1

void propertyChange(PropertyChangeEvent event)

Appele lorsquune valeur de proprit a chang.

Livre Java .book Page 385 Jeudi, 25. novembre 2004 3:04 15

9
Swing et les composants
dinterface utilisateur
Au sommaire de ce chapitre

Larchitecture Modle-Vue-Contrleur des boutons Swing


Introduction la gestion de mise en forme
Entre de texte
Composants du choix
Menus
Mise en forme sophistique
Botes de dialogue
Le chapitre prcdent a illustr essentiellement lemploi du modle dvnements de Java. Vous avez
accompli les premires tapes de llaboration dune interface utilisateur. Ce chapitre vous prsentera les principaux outils ncessaires la cration dinterfaces dotes dun plus grand nombre de
fonctionnalits.
Nous commencerons par les caractristiques de larchitecture sous-jacente de la bibliothque
Swing ; ainsi, vous pourrez en utiliser efficacement les composants les plus avancs. Nous poursuivrons avec les lments graphiques de cette bibliothque les plus couramment employs, tels que les
champs de saisie de texte, les boutons radio et les menus. Vous tudierez aussi lexploitation des
fonctionnalits de gestion de mise en forme utilises avec Java pour pouvoir disposer ces composants dans une fentre, indpendamment du style de look and feel adopt pour linterface utilisateur.
Pour finir, vous aborderez limplmentation des botes de dialogue avec Swing.
Ce chapitre couvre tous les composants Swing de base, tels que les outils de texte, les boutons et les
curseurs. Il sagit de ceux auxquels vous recourrez le plus souvent. Les composants Swing plus
sophistiqus sont tudis au Volume 2.

Livre Java .book Page 386 Jeudi, 25. novembre 2004 3:04 15

386

Au cur de Java 2 - Notions fondamentales

Larchitecture Modle-Vue-Contrleur
Avant de dcrire larchitecture sur laquelle sappuie lemploi des composants Swing, rflchissons
un peu tous les lments qui entrent dans la constitution dun composant dinterface utilisateur
comme un bouton, une case cocher, un champ de texte, ou un contrle darborescence sophistiqu.
Chaque composant possde trois caractristiques :
m

son contenu, tel que ltat dun bouton (activ ou non) ou la valeur dun champ de texte ;

son apparence (couleur, taille, etc.) ;

son comportement (raction aux vnements).

Mme un composant apparemment simple, comme un bouton, cache une interaction relativement
complexe entre ces caractristiques. Lapparence dun bouton dpend videmment du style que lon
souhaite donner. Le style Metal est visuellement diffrent du style Windows ou Motif. Cette apparence dpend aussi de ltat du bouton : lorsquil est press, il doit tre redessin pour afficher un
aspect diffrent. Cet tat dcoule des vnements quil reoit.
Bien sr, lorsque vous vous servez dun bouton dans vos programmes, vous le considrez simplement comme un bouton, sans rflchir davantage ses rouages internes. Cest au programmeur qui
la implment que revient le travail de rflexion. Il se doit de mettre en uvre tous les composants
dune interface pour quils puissent fonctionner quel que soit le look and feel de linterface install.
Pour cela, les concepteurs de Swing se sont tourns vers une architecture bien connue : Modle-VueContrleur. A linstar dautres architectures, elle repose sur lun des principes de la conception
oriente objet (voir Chapitre 5), qui prconisait de ne pas trop assigner de responsabilits un seul
objet. Evitez quune seule classe de boutons ne soit charge de tout faire. Au contraire, associez le
style dun composant un seul objet et stockez-en le contenu dans un autre objet. Larchitecture
Modle-Vue-Contrleur (MVC) nous enseigne comment raliser cette opration en implmentant
trois classes spares :
m

le modle qui stocke le contenu ;

la vue qui affiche le contenu ;

le contrleur qui gre lentre utilisateur.

Larchitecture prvoit prcisment de quelle faon ces trois objets interagissent. Le modle conserve
la valeur du composant et ne possde pas dinterface utilisateur. Pour un bouton, le contenu, ou sa
valeur, est assez ordinaire : juste un petit ensemble dindicateurs qui signalent sil est press ou non,
actif ou inactif, etc. Pour un champ de texte, le contenu est un peu plus intressant. Cest un objet de
type chane qui contient le texte courant. Il ne sagit pas de la vue du contenu ; si celui-ci est plus
grand que le champ, lutilisateur nen voit quune partie (voir Figure 9.1).
Figure 9.1

model

Modle et vue
dun champ
de texte.

view

"The quick brown fox jumps over the lazy dog"

Livre Java .book Page 387 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

387

Le modle doit implmenter des mthodes pour modifier le contenu et dcouvrir ce quil reprsente.
Par exemple, un modle texte possde des mthodes pour ajouter ou supprimer des caractres du
contenu actuel et renvoyer la valeur courante sous forme dune chane. Encore une fois, gardez
lesprit que le modle est compltement non visuel. Il incombe la vue de dessiner les donnes qui
sont stockes dans le modle.
INFO
Le choix du terme "modle" est peut-tre malheureux, car un modle est souvent interprt comme tant une reprsentation dun concept abstrait. Les concepteurs de voitures ou davions construisent des modles pour simuler les
vraies machines. Cette analogie peut tre confondante lorsque lon pense larchitecture Modle-Vue-Contrleur.
Ici, le modle conserve lintgralit du contenu et la vue en donne une reprsentation visuelle partielle ou complte.
Une meilleure analogie serait de prendre un modle qui pose pour un artiste. Il revient ce dernier dobserver le
premier et den crer une vue. Selon lartiste, la vue pourrait tre un portrait figuratif, une peinture impressionniste
ou encore un dessin cubiste reprsentant lesprit en dtranges contorsions.

Un des avantages de larchitecture Modle-Vue-Contrleur est de pouvoir possder plusieurs vues,


chacune montrant une partie ou un aspect diffrents du contenu. Par exemple, un diteur HTML peut
offrir deux vues simultanes dun mme contenu, lune WYSIWYG et lautre brute avec les balises
(voir Figure 9.2). Lorsque le modle est mis jour par lintermdiaire du contrleur de lune des
vues, il en avise les deux vues associes. Lorsque celles-ci reoivent la notification, elles sactualisent automatiquement. Bien sr, pour un simple composant dinterface, tel quun bouton, vous
nobtiendrez pas plusieurs vues du mme modle.
Figure 9.2
Deux vues
spares du
mme modle.

model

OL

LI
LI
LI

WYSIWG
view

1.
2.
3.

tag
view

<P>
<OL>
<LI>
<LI>
<LI>

</P>
</LI>
</LI>
</LI>

</OL>

Le contrleur gre les vnements dentre utilisateur tels que les clics de souris ou les frappes au clavier.
Lorsquils se produisent, le contrleur dcide de les traduire en changements dans le modle ou la vue.

Livre Java .book Page 388 Jeudi, 25. novembre 2004 3:04 15

388

Au cur de Java 2 - Notions fondamentales

Par exemple, si lutilisateur tape un caractre dans un champ de texte, le contrleur appelle la
commande dinsertion de caractre du modle. Le modle commande ensuite la vue de se mettre
jour. Celle-ci ignore toujours pourquoi le texte a chang. Mais si lutilisateur appuie sur une touche,
le contrleur peut indiquer la vue de dfiler. Ce dfilement na aucun effet sur le texte sous-jacent,
le modle ne sait donc jamais que cet vnement sest produit.

Modles de conception
Lors de la rsolution dun problme, vous nbauchez gnralement pas une solution partir des
principes de base. Vous tes plus vraisemblablement guid par lexprience, ou vous pouvez
demander dautres experts un conseil. Les modles de conception constituent un mode de
prsentation structur de cette expertise.
Au cours des dernires annes, les concepteurs de logiciels ont commenc rassembler de tels
modles. Les pionniers dans ce domaine ont t inspirs par les modles de conception architecturale de Christopher Alexander. Dans son livre The Timeless Way of Building (Oxford University
Press 1979), il fournit une collection de modles pour la conception des espaces de vie publics et
privs. En voici un exemple typique :
Espace fentre
Tout le monde aime les places prs des fentres, les baies vitres, ou encore avec de larges rebords
de faible hauteur, avec des fauteuils confortables proximit. Une pice qui ne dispose pas dun
tel espace ne vous permettra pas de vous sentir bien install ou parfaitement laise.
La pice dpourvue dune fentre qui permettrait damnager un tel "espace de vie" soumettra
son occupant un dilemme :
1. Sasseoir et tre confortablement install.
2. Etre proche de la lumire.
Si les espaces confortables ceux o vous avez envie de vous asseoir sont loigns des fentres,
il nexiste alors aucune possibilit de rsoudre ce conflit.
Par consquent, dans chaque pice o vous sjournez souvent, transformez au moins une fentre
en un "espace fentre".
Figure 9.3
Un espace fentre.

low
sill
place

Chaque modle, dans le catalogue dAlexander ainsi que dans les catalogues de modles logiciels,
suit un format particulier. Il dcrit tout dabord un contexte, une situation qui donne lieu un
problme de conception. Celui-ci est ensuite expliqu, gnralement sous la forme dun ensemble
de forces en conflit. Finalement, la solution indique une configuration qui quilibre ces forces.
Dans le modle "espace fentre", le contexte est une pice dans laquelle vous passez un certain
temps dans la journe. Les forces en conflit sont le fait que vous souhaitez vous asseoir confortablement, et le fait dtre attir par la lumire. La solution est de fabriquer un "espace fentre".

Livre Java .book Page 389 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

389

Dans larchitecture Modle-Vue-Contrleur, le contexte est un systme dinterface utilisateur qui


prsente des informations et reoit les entres de lutilisateur. Il y a plusieurs forces. Il peut y avoir
plusieurs reprsentations visuelles des mmes donnes qui doivent tre mises jour ensemble. La
reprsentation visuelle peut changer, par exemple pour sadapter divers look and feel standard.
Les mcanismes dinteraction peuvent aussi changer, par exemple pour grer des commandes
vocales. La solution consiste rpartir les responsabilits en trois composants spars qui interagissent : le modle, la vue et le contrleur.
Bien sr, cette architecture est plus complexe que le modle "espace fentre" ; il est ncessaire dtudier
certains dtails pour que cette rpartition des responsabilits puisse fonctionner correctement.
Larchitecture Modle-Vue-Contrleur nest pas le seul modle utilis dans la conception de Java. Par
exemple, le mcanisme de gestion dvnements dAWT suit le modle dcouteurs dvnement.
Un aspect important des modles de conception est quils font maintenant partie de la culture. Les
programmeurs travers le monde comprennent si vous parlez de larchitecture Modle-VueContrleur ou du modle dcouteurs dvnement. Les modles darchitecture sont un moyen
efficace de discuter des problmes de conception.

La Figure 9.4 illustre les interactions entre les objets modle, vue et contrleur.
Figure 9.4
Interactions entre
les objets modles, vue
et contrleur.

Controller

View

Model

paint view
read content

update content
content changed

update view

Livre Java .book Page 390 Jeudi, 25. novembre 2004 3:04 15

390

Au cur de Java 2 - Notions fondamentales

En tant que programmeur utilisant les composants Swing, vous navez pas besoin de penser
larchitecture Modle-Vue-Contrleur. Chaque interface dispose dune classe enveloppe, ou
wrapper, comme JButton ou JTextField qui stocke le modle et la vue. Lorsque vous souhaitez connatre le contenu dun composant, par exemple dun champ de texte, la classe enveloppe
transmet cette requte la vue. Toutefois, il existe des occasions o la classe ne fonctionne pas
de manire idale pour la transmission des commandes. Vous devez alors lui demander de rcuprer
le modle pour agir directement avec ce dernier. Vous navez pas besoin de travailler directement avec la vue, car cette tche incombe au code qui est li au style dinterface implment
(LookAndFeel).
Outre la possibilit dexcuter laction approprie, cette architecture permet aux concepteurs
dimplmenter un style dinterface dynamique, le plaf ou pluggable look and feel. Le modle dun
bouton ou dun champ de texte est indpendant du style dinterface, mais sa reprsentation visuelle
dpend totalement de la conception dune interface de style particulier. Le contrleur peut galement
varier. Par exemple, sur une machine contrle par la voix, le contrleur doit faire face un ensemble dvnements totalement diffrent de celui dun ordinateur standard muni dun clavier et dune
souris. En sparant le modle sous-jacent de linterface utilisateur, les concepteurs de Swing peuvent
rutiliser le code des modles et mme passer dun look and feel un autre au cours dun programme
en cours dexcution.
Bien sr, les modles sont prvus pour servir de guides ; vous ntes pas forc de les suivre la
lettre. Ils ne sont pas applicables dans toutes les situations. Par exemple, vous pouvez prouver des
difficults suivre le modle "espace de fentre" (voir plus haut) pour rorganiser votre studette. De
la mme manire, les concepteurs Swing se sont aperus que dans la ralit, limplmentation dun
style dynamique dinterface ne permet pas toujours une ralisation propre de larchitecture ModleVue-Contrleur. Les modles sont faciles distinguer ; chaque composant dinterface possde une
classe de modle. Mais les responsabilits de la vue et du contrleur ne sont pas toujours clairement
spares et sont rparties travers un certain nombre de classes diffrentes. Bien sr, en tant quutilisateur de ces classes, vous ne serez pas concern par tout cela. En fait, comme nous lavons signal,
vous naurez pas vous soucier des modles ; vous pourrez simplement exploiter les classes enveloppes du composant.

Une analyse Modle-Vue-Contrleur des boutons Swing


Vous avez dj appris utiliser des boutons au chapitre prcdent sans avoir vous proccuper
des objets contrleur, modle ou vue. Les boutons comptent parmi les lments dinterface les
plus simples ; nous les utiliserons donc pour nous familiariser avec cette architecture. Vous
rencontrerez des types de classes et dinterfaces similaires pour les composants Swing plus
sophistiqus.
Pour la plupart des composants, la classe modle implmente une interface dont le nom se
termine par Model, do linterface appele ButtonModel. Les classes limplmentant peuvent
dfinir ltat des divers types de boutons. En fait, les boutons ne sont pas aussi complexes et la
bibliothque Swing contient une unique classe appele DefaultButtonModel qui implmente
cette interface.

Livre Java .book Page 391 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

391

Pour connatre le type des donnes qui sont gres par un modle de boutons, examinez les mthodes
de linterface ButtonModel. Le Tableau 9.1 prsente les mthodes daccs (accessor).
Tableau 9.1 : Les mthodes daccs de linterface ButtonModel

getActionCommand()

La chane de commande daction associe ce bouton

getMnemonic()

Le raccourci clavier pour ce bouton

isArmed()

true si le bouton a t press et que la souris est toujours au-dessus du bouton

isEnabled()

true si le bouton peut tre slectionn

isPressed()

true si le bouton de commande a t press, mais que le bouton de la souris


na pas encore t relch

isRollover()

true si la souris se trouve au-dessus du bouton

isSelected()

true si ltat du bouton a t bascul (pour les cases cocher et les boutons
radio)

Chaque objet JButton stocke un objet modle de bouton que vous pouvez rcuprer :
JButton button = new JButton("Blue");
ButtonModel model = button.getModel();

Dans la pratique, vous navez pas vous en proccuper les dtails de ltat du bouton nintressent que la vue qui le dessine. Les informations importantes, telles que savoir si un bouton est activ,
sont disponibles au moyen de la classe JButton (bien sr, cest lobjet JButton qui demande son
modle dobtenir ces informations).
Examinez linterface ButtonModel pour noter ce qui manque. Le modle ne conserve pas le
libell ou licne du bouton. Il est impossible de savoir ce qui se trouve en surface dun bouton
par un simple examen de son modle. En fait, comme vous le constaterez dans la section relative
aux groupes de boutons radio, ce ct pur de la conception est une source dennuis pour le
programmeur.
Il importe galement de signaler que le mme modle, en loccurrence DefaultButtonModel, est
utilis pour les boutons de commande, les boutons radio, les cases cocher et mme les options de
menu. Bien sr, chacun de ces types de boutons possde des vues et des contrleurs diffrents. Lorsque le style dinterface Metal est employ, lobjet JButton utilise une classe appele BasicButtonUI pour la vue, et une classe appele ButtonUIListener comme contrleur. En gnral, chaque
composant Swing possde un objet vue associ qui se termine par les lettres UI. Mais tous les
composants Swing ne possdent pas dobjets contrleur ddis.
Aprs cette brve introduction sur les particularits sous-jacentes de JButton, vous vous interrogez
sans doute sur la nature relle de cet objet : en fait, il sagit dune classe enveloppe drive de la
classe JComponent qui contient lobjet DefaultButtonModel, certaines donnes de vue comme
le libell et les icnes et lobjet BasicButtonUI, responsable de la vue du bouton.

Livre Java .book Page 392 Jeudi, 25. novembre 2004 3:04 15

392

Au cur de Java 2 - Notions fondamentales

Introduction la gestion de mise en forme


Avant de poursuivre avec dautres composants Swing, comme les champs de texte et les boutons
radio, nous devons brivement traiter de la faon dont les composants peuvent tre disposs dans un
cadre. A la diffrence de VisualBasic, le JDK ne possde pas de concepteur de formulaire. Vous
devez crire du code pour positionner les composants dinterface.
Bien sr, si vous disposez dun environnement de dveloppement qui accepte Java, il possdera
probablement un outil de mise en forme pour automatiser certaines de ces tches, ou la totalit.
Nanmoins, il importe de matriser le processus interne de telles oprations, car mme le meilleur de
ces outils ncessitera une intervention manuelle.
Commenons par rexaminer le programme du dernier chapitre qui utilise des boutons pour modifier
la couleur de fond dune fentre (voir Figure 9.5).
Figure 9.5
Un panneau
avec trois boutons.

Nous avons construit ce programme de la faon suivante :


1. Nous avons dfini lapparence de chaque bouton en passant au constructeur une chane pour le
libell, par exemple :
JButton yellowButton = new JButton("Yellow");

2. Puis nous avons ajout les boutons individuels un panneau, par exemple avec la ligne :
Panel.add(yellowButton);

3. Ensuite, nous avons ajout les gestionnaires dvnement ncessaires, comme :


yellowButton.addActionListener(listener);

Que se passe-t-il si nous augmentons le nombre de boutons ? La Figure 9.6 montre ce qui se
produit avec six boutons dans la fentre. Comme vous pouvez le constater, ils sont centrs horizontalement sur une ligne, et lorsquil ny a plus de place, une nouvelle ligne est commence.
De plus, les boutons restent centrs mme lorsque lutilisateur redimensionne la fentre (voir
Figure 9.7).
Un concept trs lgant permet cette mise en forme dynamique. Tous les composants dans un conteneur sont positionns par un gestionnaire de mise en forme (Layout). Dans notre exemple, les
boutons sont grs par FlowLayout, le gestionnaire par dfaut pour un panneau JPanel.

Livre Java .book Page 393 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

393

Figure 9.6
Une fentre avec six boutons grs par le gestionnaire FlowLayout.

Figure 9.7
Le changement de taille
de la fentre provoque la
rorganisation automatique des boutons.

Ce gestionnaire aligne horizontalement les composants jusqu ce quil ny ait plus de place et
commence une nouvelle ligne.
Si lutilisateur modifie la taille du conteneur, le gestionnaire de mise en forme rorganise automatiquement les composants en fonction de lespace disponible.
Vous pouvez choisir la disposition des composants sur chaque ligne. Par dfaut, ils sont centrs horizontalement par rapport au conteneur. Les autres choix sont un alignement sur la gauche ou sur la
droite du conteneur. Pour exploiter un de ces alignements, spcifiez les constantes symboliques LEFT
ou RIGHT dans le constructeur de lobjet FlowLayout:
panel setLayout(new FlowLayout(FlowLayout.LEFT);

INFO
Normalement, vous laissez le gestionnaire contrler simplement les intervalles verticaux et horizontaux entre les
diffrents composants. Vous pouvez toutefois imposer un intervalle horizontal ou vertical spcifique laide dune
autre version du constructeur du gestionnaire (voir les notes API).

java.awt.Container 1.0

setLayout(LayoutManager m)

Configure le gestionnaire de mise en forme pour ce conteneur.

Livre Java .book Page 394 Jeudi, 25. novembre 2004 3:04 15

394

Au cur de Java 2 - Notions fondamentales

java.awt.FlowLayout 1.0

FlowLayout(int align)

Construit un nouveau gestionnaire FlowLayout avec lalignement spcifi.


Paramtres :

align

Lune des constantes dalignement LEFT, CENTER ou RIGHT


(gauche, centr ou droite).

FlowLayout(int align, int hgap, int vgap)

Construit un nouveau gestionnaire FlowLayout avec lalignement et les intervalles horizontaux


et verticaux spcifis.
Paramtres :

align

Lune des constantes dalignement LEFT, CENTER ou RIGHT.

hgap

Lintervalle horizontal en pixels utiliser (les valeurs


ngatives provoquent un chevauchement).

vgap

Lintervalle vertical en pixels utiliser (les valeurs ngatives


provoquent un chevauchement).

Gestionnaire BorderLayout
Java fournit plusieurs gestionnaires de mise en forme ; vous pouvez mme construire vos
propres gestionnaires. Nous aborderons tous ces aspects plus loin dans ce chapitre. Toutefois,
afin de vous donner immdiatement des exemples plus intressants, nous devons brivement
dcrire un autre gestionnaire appel BorderLayout. Il sagit du gestionnaire par dfaut du
panneau de contenu contentPane de chaque JFrame. A la diffrence de FlowLayout qui
contrle compltement la position de chaque composant, BorderLayout vous permet de choisir
lemplacement de chaque composant par lintermdiaire dune constante de positionnement
sinspirant des points cardinaux : au centre, au nord, au sud, lest ou louest du panneau
conteneur (voir Figure 9.8).
Figure 9.8
Le gestionnaire BorderLayout.

North

West

Center

East

South

Par exemple :
panel.setLayout(new BorderLayout());class MyPanel extends JPanel
panel.add(yellowButton, BorderLayout.SOUTH);

Livre Java .book Page 395 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

395

Les composants en bordure sont placs en premier et lespace disponible restant est occup par le
centre. Lorsque le conteneur est redimensionn, la densit des composants en lisire ne change pas,
mais le composant central voit ses dimensions modifies. Vous ajoutez un composant en spcifiant
une constante CENTER, NORTH, SOUTH, EAST ou WEST de la classe BorderLayout. Toutes ces positions
ne doivent pas ncessairement tre occupes. Si vous ne fournissez aucune chane, la constante
"Center" est utilise.
INFO
Les constantes BorderLayout sont dfinies comme des chanes. Par exemple, BorderLayout.SOUTH est dfini
comme la chane "South". De nombreux programmeurs prfrent utiliser directement les chanes, plus courtes ; par
exemple frame.add(component, "South"). Cependant, si vous faites une erreur en crivant la chane, le compilateur ne pourra pas la dtecter.

A la diffrence du gestionnaire FlowLayout qui prserve la taille des composants, BorderLayout en


augmente la taille pour remplir lespace disponible.
A linstar des gestionnaires FlowLayout, si vous souhaitez spcifier un intervalle entre les diffrentes
rgions, vous pouvez le faire dans le constructeur de la classe BorderLayout.
Comme indiqu prcdemment, lobjet conteneur dune fentre JFrame possde un gestionnaire
BorderLayout. Jusqu prsent, nous nen avons pas tir avantage ; nous navons ajout les
panneaux que dans la zone par dfaut, le centre. Mais vous pouvez aussi ajouter des composants
dans les autres zones :
frame.add(yellowButton, BorderLayout.SOUTH);

Ce fragment de code pose toutefois un problme que nous verrons dans la prochaine section.
java.awt.Container 1.0

void add(Component c, Object constraints) 1.1

Ajoute un composant dans le conteneur.


Paramtres :

Le composant ajouter.

constraints

Un identifiant accept par le gestionnaire de mise en forme.

java.awt.BorderLayout 1.0

BorderLayout(int hgap, int vgap)

Construit un nouveau gestionnaire BorderLayout avec les intervalles horizontaux et verticaux


spcifis entre les composants.
Paramtres :

hgap

Lintervalle horizontal en pixels utiliser (les valeurs


ngatives provoquent un chevauchement).

vgap

Lintervalle vertical en pixels utiliser (les valeurs ngatives


provoquent un chevauchement).

Livre Java .book Page 396 Jeudi, 25. novembre 2004 3:04 15

396

Au cur de Java 2 - Notions fondamentales

Panneaux
Un gestionnaire BorderLayout nest pas trs utile en tant que tel. La Figure 9.9 illustre ce qui se
produit lorsque vous utilisez le fragment de code ci-dessus. Le bouton a t agrandi pour remplir
toute la rgion sud de la fentre. Si vous en ajoutiez un autre dans la mme rgion, il dplacerait le
premier.
Une mthode couramment employe pour contourner ce problme est lusage des panneaux supplmentaires. Les panneaux agissent comme des petits conteneurs pour les lments dinterface, qui
peuvent leur tour tre disposs dans un panneau plus grand soumis au contrle dun gestionnaire
de mise en forme. Par exemple, vous pouvez avoir un panneau dans la zone sud pour les boutons et
un autre au centre pour le texte. En imbriquant les panneaux et en utilisant une combinaison de
gestionnaires BorderLayout et FlowLayout, vous pouvez obtenir un positionnement relativement
prcis des diffrents composants. Cette faon de procder est certainement suffisante pour laborer
un prototype ; cest aussi la mthode que nous adopterons pour les programmes cits en exemple
dans la premire partie de ce chapitre. Reportez-vous la section sur le gestionnaire GridBagLayout
pour dcouvrir un moyen de positionnement plus prcis des composants.
Figure 9.9
Un seul bouton gr par
un gestionnaire BorderLayout.

Par exemple, examinez la Figure 9.10. Les trois boutons la base de la fentre sont contenus dans un
panneau qui est plac au sud du conteneur.
Figure 9.10
Un panneau plac au sud
du cadre.

Supposons que vous vouliez ajouter un panneau avec trois boutons (voir Figure 9.10). Crez dabord
un nouvel objet JPanel avant lajout de chaque bouton. Le gestionnaire de mise en forme par dfaut
pour un panneau est FlowLayout, ce qui convient dans notre cas. Vous ajouterez ensuite les boutons
individuels au moyen de la mthode add dj tudie. Comme vous ajoutez des boutons un
panneau et ne changez pas le gestionnaire de mise en forme par dfaut FlowLayout, la position et la
taille des boutons sont soumises son contrle. Ainsi, ils resteront centrs par rapport au panneau et

Livre Java .book Page 397 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

397

ne sagrandiront pas pour occuper toute sa surface. Voici un extrait de code qui permet dajouter un
panneau avec trois boutons lextrmit sud du cadre :
JPanel panel = new JPanel();
panel.add(yellowButton);
panel.add(blueButton);
panel.add(redButton);
frame.add(panel, BorderLayout.SOUTH);

INFO
Les frontires du panneau sont invisibles lutilisateur. Les panneaux ne constituent quun mcanisme dorganisation
pour le concepteur de linterface utilisateur.

Pour mmoire, la classe JPanel utilise par dfaut un gestionnaire de mise en forme FlowLayout.
Pour un conteneur JPanel, vous pouvez fournir un objet gestionnaire de mise en forme diffrent
dans le constructeur. Mme si la plupart des autres conteneurs ne possdent pas de constructeur, tous
les conteneurs disposent de la mthode setLayout pour dfinir un gestionnaire de mise en forme
autre que celui par dfaut du conteneur.
javax.swing.JPanel 1.2

JPanel(LayoutManager m)

Dfinit le gestionnaire de mise en forme pour le panneau.

Disposition des grilles


La disposition des grilles organise les composants, un peu la manire des lignes et des colonnes
dun tableur. Toutefois, toutes les lignes et les colonnes de la grille ont une taille identique. Le
programme de calculatrice (voir Figure 9.11) emploie la disposition de grille pour en organiser les
boutons. Lorsque vous redimensionnez la fentre, les boutons sagrandissent et diminuent tout en
conservant des tailles identiques.
Figure 9.11
Une calculette.

Dans le constructeur de lobjet GridLayout, vous spcifiez le nombre de lignes et de colonnes


requis :
panel.setLayout(new GridLayout(5, 4));

Livre Java .book Page 398 Jeudi, 25. novembre 2004 3:04 15

398

Au cur de Java 2 - Notions fondamentales

A limage des gestionnaires BorderLayout et FlowLayout, vous pouvez aussi spcifier les intervalles
verticaux et horizontaux souhaits :
panel.setLayout(new GridLayout(5, 4, 3, 3));

Les deux derniers paramtres de ce constructeur spcifient la taille des intervalles horizontaux et
verticaux (en pixels) entre les composants.
Vous ajoutez les composants en commenant par la premire entre sur la premire ligne, puis la
deuxime sur la mme ligne, etc. :
panel.add(new JButton("1"));
panel.add(new JButton("2"));

LExemple 9.1 prsente le code source du programme de la calculette. Il sagit dune calculatrice
ordinaire, et non dune version qui utilise la notation "polonaise inverse", si populaire dans les
didacticiels Java. Dans ce programme, nous appelons la mthode pack aprs avoir ajout le composant au cadre. Cette mthode emploie les tailles prfres de tous les composants pour calculer la
largeur et la hauteur du cadre.
Bien sr, peu dapplications possdent une mise en forme aussi rigide que la faade dune calculatrice. Dans la pratique, de petites grilles (contenant gnralement une seule ligne ou colonne)
permettent dorganiser des zones partielles dune fentre. Si vous voulez disposer dune ligne de
boutons de taille identique, vous pouvez les placer lintrieur dun panneau gr par un gestionnaire GridLayout avec une seule ligne.
Exemple 9.1 : Calculator.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Calculator
{
public static void main(String[] args)
{
CalculatorFrame frame = new CalculatorFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau pour une calculatrice.
*/
class CalculatorFrame extends JFrame
{
public CalculatorFrame()
{
setTitle("Calculator");
CalculatorPanel panel = new CalculatorPanel();
add(panel);
pack();
}
}

Livre Java .book Page 399 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

/**
Un panneau avec les boutons de la calculatrice
et laffichage des rsultats.
*/
class CalculatorPanel extends JPanel
{
public CalculatorPanel()
{
setLayout(new BorderLayout());
result = 0;
lastCommand = "=";
start = true;
// ajouter la zone daffichage
display = new JButton("0");
display.setEnabled(false);
add(display, BorderLayout.NORTH);
ActionListener insert = new InsertAction();
ActionListener command = new CommandAction();
// ajouter les boutons dans une grille 4x4
panel = new JPanel();
panel.setLayout(new GridLayout(4, 4));
addButton("7",
addButton("8",
addButton("9",
addButton("/",

insert);
insert);
insert);
command);

addButton("4",
addButton("5",
addButton("6",
addButton("*",

insert);
insert);
insert);
command);

addButton("1",
addButton("2",
addButton("3",
addButton("-",

insert);
insert);
insert);
command);

addButton("0",
addButton(".",
addButton("=",
addButton("+",

insert);
insert);
command);
command);

add(panel, BorderLayout.CENTER);
}
/**
ajoute un bouton au panneau central.
@param label Le libell du bouton
@param listener Lcouteur du bouton
*/

399

Livre Java .book Page 400 Jeudi, 25. novembre 2004 3:04 15

400

Au cur de Java 2 - Notions fondamentales

private void addButton(String label, ActionListener listener)


{
JButton button = new JButton(label);
button.addActionListener(listener);
panel.add(button);
}
/**
Cette action insre la chane daction du bouton
la fin du texte daffichage.
*/
private class InsertAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String input = event.getActionCommand();
if (start)
{
display.setText("");
start = false;
}
display.setText(display.getText() + input);
}
}
/**
Cette action excute la commande indique par la chane
daction du bouton.
*/
private class CommandAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String command = event.getActionCommand();
if (start)
{
if (command.equals("-"))
{
display.setText(command);
start = false;
}
else
lastCommand = command;
}
else
{
calculate(Double.parseDouble(display.getText()));
lastCommand = command;
start = true;
}
}
}
/**
Excute le calcul en attente.
@param x La valeur cumuler avec le rsultat prcdent.
*/
public void calculate(double x)

Livre Java .book Page 401 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

401

{
if (lastCommand.equals("+")) result += x;
else if (lastCommand.equals("-")) result -= x;
else if (lastCommand.equals("*")) result *= x;
else if (lastCommand.equals("/")) result /= x;
else if (lastCommand.equals("=")) result = x;
display.setText("" + result);
}
private
private
private
private
private

JButton display;
JPanel panel;
double result;
String lastCommand;
boolean start;

}
java.awt.GridLayout 1.0

GridLayout(int row, int cols)

Construit une nouvelle grille GridLayout.


Paramtres :

rows

Le nombre de lignes dans la grille.

columns

Le nombre de colonnes dans la grille.

GridLayout(int rows, int columns, int hgap, int vgap)

Construit une nouvelle grille GridLayout avec des intervalles horizontaux et verticaux entre les
composants.
Paramtres :

rows

Le nombre de lignes dans la grille.

columns

Le nombre de colonnes dans la grille.

hgap

Lintervalle horizontal en pixels (les valeurs ngatives


provoquent un chevauchement).

vgap

Lintervalle vertical en pixels (les valeurs ngatives provoquent


un chevauchement).

java.awt.Window 1.0

void pack()

Redimensionne la fentre, en prenant en compte les tailles prfres de ses composants.

Entre de texte
Nous sommes prts pour lintroduction des composants Swing dinterface utilisateur. Vous pouvez
utiliser les composants JTextField et JTextArea pour collecter les entres de texte. Un champ de
texte naccepte quune ligne de texte, et les zones de texte peuvent en accueillir plusieurs. Ces deux
classes drivent dune classe appele JTextComponent. Vous ne pourrez pas construire vous-mme
un objet JTextComponent, car il sagit dune classe abstract. En revanche, cas frquent avec Java
lors de la lecture de la documentation sur les API, vous constaterez que les mthodes recherches se
trouveront plus souvent dans la classe parent JTextComponent que dans les classes drives. Par
exemple, les mthodes qui permettent de rcuprer ou de dfinir le texte dans un champ ou une zone
de texte sont en ralit des mthodes de la classe JTextComponent.

Livre Java .book Page 402 Jeudi, 25. novembre 2004 3:04 15

402

Au cur de Java 2 - Notions fondamentales

javax.swing.text.JTextComponent 1.2

void setText(String t)

Modifie le texte dun composant texte.


Paramtre :

Le nouveau texte.

String getText()

Renvoie le texte contenu dans le composant texte.

void setEditable(boolean b)

Dtermine si lutilisateur peut modifier le contenu de lobjet JTextComponent.

Champs de texte
La mthode pour ajouter un champ de texte dans une fentre consiste lajouter un panneau ou un
conteneur, comme pour un bouton :
JPanel panel = new JPanel();
JTextField textField = new JTextField("Default input", 20).
panel.add(textField);

Ce code ajoute un champ de texte et linitialise avec la chane "Default input". Le second paramtre de ce constructeur en dfinit la largeur. Dans notre exemple, la largeur est de 20 "colonnes".
Malheureusement, une colonne est une unit de mesure assez imprcise. Elle reprsente la largeur
attendue dun caractre dans la police employe pour le texte. Si vous prvoyez des entres utilisateur dau plus n caractres, vous tes suppos indiquer n en tant que largeur de colonne. Dans la
pratique, cette mesure ne donne pas de trs bons rsultats et vous devrez ajouter 1 ou 2 la longueur
dentre maximale prvue. Gardez aussi lesprit que le nombre de colonnes nest quune suggestion pour AWT pour indiquer une taille de prfrence. Si le gestionnaire de mise en forme a besoin
dagrandir ou de rduire le champ de texte, il peut en ajuster la taille. La largeur de colonne que vous
dfinissez dans le constructeur de la classe JTextField ne limite pas pour autant le nombre de
caractres que lutilisateur peut taper. Il peut introduire des chanes plus longues ; toutefois, la vue
de lentre dfile lorsque le texte dpasse la longueur du champ, ce qui est assez irritant pour lutilisateur ; prvoyez donc gnreusement lespace ncessaire. Si vous devez redfinir le nombre de
colonnes durant lexcution, vous pouvez le faire avec la mthode setColumns.
ASTUCE
Aprs avoir chang la taille dun champ de texte avec la mthode setColumns, vous devez appeler la mthode
revalidate du conteneur :
textField.setColumns(10);
panel.revalidate();

Cette mthode recalcule la taille et la disposition de tous les composants dans un conteneur. Aprs son utilisation, le
gestionnaire de mise en forme redimensionne le conteneur avec la taille du champ modifie.
La mthode revalidate appartient la classe JComponent. Elle ne redimensionne pas immdiatement le composant, elle le signale simplement pour un redimensionnement ultrieur. Cette approche vite des calculs rptitifs
lorsquil faut redimensionner plusieurs composants. Si vous souhaitez toutefois recalculer tous les composants dans
un JFrame, vous devez appeler la mthode validate (JFrame ntend pas JComponent).

Livre Java .book Page 403 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

403

En gnral, vous autorisez lutilisateur ajouter du texte (ou modifier le texte existant) dans les
champs de texte ; ceux-ci apparaissent donc vides le plus souvent lors du premier affichage. Pour
quun champ soit vide, il suffit de ne pas passer de chane pour le paramtre concern du constructeur de
JTextField:
JTextField textField=new JTextField(20);

Vous pouvez modifier le contenu du champ de texte tout moment avec la mthode setText de la
classe parent TextComponent mentionne dans la section prcdente. Par exemple :
textField.setText("Hello!");

De plus, vous pouvez dterminer ce que lutilisateur a tap en appelant la mthode getText. Elle
renvoie tout ce que lutilisateur a entr, y compris les espaces en tte et en fin de chane. Vous pouvez
les supprimer grce la mthode trim lors de la rcupration de lentre :
String Text = textField.getText().trim();

Pour modifier la police du texte saisi par lutilisateur, appelez la mthode setFont.
javax.swing.JTextField 1.2

JTextField(int cols)

Construit un JTextField vide avec un nombre spcifi de colonnes.


Paramtres :

cols

Le nombre de colonnes dans le champ

JTextField(String text, int cols)

Construit un nouveau JTextField avec une chane initiale et le nombre spcifi de colonnes.
Paramtres :

text

Le texte afficher

cols

Le nombre de colonnes

void setColumns(int cols)

Indique au champ texte le nombre de colonnes utiliser.


Paramtres :

cols

Le nombre de colonnes

javax.swing.JComponent 1.2

void revalidate()

Entrane un nouveau calcul de la position et de la taille dun composant.


java.awt.Component 1.0

void validate()

Recalcule la position et la taille dun composant. Si le composant est un conteneur, les positions
et tailles de ses composants sont recalcules.

Etiquettes et composants dtiquetage


Les tiquettes (ou libells) sont des composants qui contiennent du texte. Ils ne possdent pas
dornements, telle une bordure, et ne ragissent pas aux entres de lutilisateur. Vous pouvez
employer une tiquette pour identifier des composants comme les composants de texte, qui,

Livre Java .book Page 404 Jeudi, 25. novembre 2004 3:04 15

404

Au cur de Java 2 - Notions fondamentales

contrairement aux boutons, nont pas de libell. Pour associer une tiquette un composant,
procdez ainsi :
1. Construisez un composant JLabel avec le texte voulu.
2. Placez-le suffisamment prs du composant identifier pour viter toute ambigut.
Le constructeur dun objet JLabel permet de spcifier le texte initial ou licne et, en option,
lalignement du contenu. Vous pouvez utiliser linterface SwingConstants pour spcifier lalignement grce aux constantes suivantes quelle dfinit : LEFT, RIGHT, CENTER, NORTH, EAST, etc. La
classe JLabel est lune des classes Swing qui implmentent cette interface. Par consquent, vous
pouvez spcifier que le texte dune tiquette soit align sur la droite au moyen de lune des deux
mthodes suivantes :
JLabel label = new JLabel("Minutes", SwingConstants.RIGHT);

ou :
JLabel label = new JLabel("Minutes", JLabel.RIGHT);

Les mthodes setText et setIcon vous permettent de dfinir le texte et licne de ltiquette au
moment de lexcution.
ASTUCE
A partir de la version 1.3 du JDK, vous pouvez avoir recours au texte normal ou HTML pour les boutons, les tiquettes
et les options de menu. Lusage du format HTML nest pas recommand pour les boutons il interfre avec le look
and feel. Il peut en revanche tre trs efficace pour les tiquettes. Encadrez simplement la chane dtiquettes avec
les balises <html>. . .</html>, de la faon suivante :
label = new JLabel("<html><b>Required</b> entry:</html>");

Sachez cependant que le premier composant avec une tiquette HTML prend un certain temps avant dtre affich,
du fait du chargement du code de rendu HTML, plutt complexe.

Les tiquettes peuvent tre positionnes lintrieur dun conteneur, linstar de tout autre composant.
Ainsi, vous pouvez exploiter les techniques dj tudies pour les disposer.
javax.swing.JLabel 1.2

JLabel(String text)

Construit une tiquette avec le texte align gauche.


Paramtres :

text

Le texte de ltiquette.

JLabel(Icon icon)

Construit une tiquette avec une icne aligne gauche.


Paramtres :

icon

Licne de ltiquette.

JLabel(String text, int align)

Paramtres :

text

Le texte de ltiquette.

align

Lune des constantes LEFT, CENTER ou RIGHT.

Livre Java .book Page 405 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

405

JLabel(String text, Icon icon, int align)

Construit une tiquette avec un texte et une icne place gauche du texte.
Paramtres :

Le texte de ltiquette.

icon

Licne de ltiquette.

align

Lune des constantes SwingConstants LEFT, CENTER


ou RIGHT.

void setText(String text)

Paramtres :

text

text

Le texte de ltiquette.

void setIcon(Icon icon)

Paramtres :

icon

Licne de ltiquette.

Suivi des modifications dans les champs de texte


Nous allons maintenant utiliser quelques champs de texte. La Figure 9.12 prsente lapplication de
lExemple 9.2, il sagit dune horloge et de deux champs de texte qui permettent de saisir les heures
et les minutes. Ds que le contenu des champs est modifi, lhorloge est mise lheure.
Figure 9.12
Exemple de champs
de texte.

Garder la trace de tout changement intervenant dans les champs de texte ncessite des efforts supplmentaires. Tout dabord, sachez que surveiller les frappes au clavier ne suffit pas. Certaines touches,
telles que les touches flches, ne modifient pas le texte. De plus, selon le style de look and feel
implment, certaines actions de la souris peuvent provoquer des modifications dans les champs.
Vous lavez tudi au dbut de ce chapitre, le champ de texte Swing est implment via une mthode
gnrique : la chane que vous voyez dans le champ nest quune manifestation visuelle (la vue)
dune structure de donnes sous-jacente (le modle). Bien sr, pour un simple champ de texte, il
nexiste pas de diffrence importante entre ces deux concepts. La vue est une chane affiche et le
modle est un objet chane. Toutefois, cest cette mme architecture qui est utilise dans les composants ddition plus avancs pour prsenter du texte format avec des polices, des paragraphes et
dautres attributs, reprsents en interne par une structure de donnes plus complexe. Le modle
pour tous les composants de texte est dcrit par linterface Document qui concerne aussi bien du

Livre Java .book Page 406 Jeudi, 25. novembre 2004 3:04 15

406

Au cur de Java 2 - Notions fondamentales

texte simple que format, comme HTML. En fait, vous pouvez interroger le document (et non le
composant texte) pour tre inform des changements, en prvoyant un couteur de document :
textField.getDocument().addDocumentListener(listener);

Lorsque le texte a chang, lune des mthodes DocumentListener suivantes est appele :
void insertUpdate(DocumentEvent event)
void removeUpdate(DocumentEvent event)
void changedUpdate(DocumentEvent event)

Les deux premires mthodes sont appeles lorsque des caractres ont t insrs ou supprims. La
troisime mthode nest pas appele pour les champs de texte. Pour des documents plus complexes,
elle sera appele pour certains types de modifications, tel quun changement de mise en forme.
Malheureusement, il ny a pas de fonction de rappel unique pour vous indiquer que le texte a t
modifi gnralement vous ne vous proccupez pas de la faon dont il a chang. Il nexiste pas
non plus de classe adapter. Ainsi, lcouteur de document doit implmenter les trois mthodes.
Voici ce qui se passe dans notre exemple de programme :
private class ClockFieldListener implements DocumentListener
{
public void insertUpdate(DocumentEvent event) { setClock(); }
public void removeUpdate(DocumentEvent event) { setClock(); }
public void changedUpdate(DocumentEvent event) {}
}

La mthode setClock appelle la mthode getText pour obtenir les chanes tapes par lutilisateur.
Pour notre programme, nous devons convertir les chanes en entiers au moyen de la fastidieuse, mais
familire, formule :
int hours = Integer.parseInt(hourField.getText().trim());
int minutes = Integer.parseInt(minuteField.getText().trim());

Ce code ne fonctionnera toutefois pas correctement si lutilisateur tape une chane telle que "deux",
qui ne reprsente pas un chiffre entier, ou sil laisse le champ de texte vierge. La mthode parseInt
dclenche alors lexception NumberFormatException. Nous allons pour linstant lintercepter et ne
pas mettre jour lhorloge si lutilisateur nentre pas un nombre. Dans la prochaine section, vous
verrez comment empcher lutilisateur de saisir une entre non valide.
INFO
Au lieu dcouter les vnements de document, vous pouvez aussi ajouter un couteur daction pour un champ de
texte. Celui-ci est notifi lorsque lutilisateur appuie sur la touche Entre. Nous ne recommandons pas cette approche, car les utilisateurs oublient parfois dappuyer sur Entre lorsquils ont fini dinscrire des donnes. Si vous
employez un tel couteur, vous devrez galement prvoir un couteur de focus pour pouvoir dterminer quand
lutilisateur quitte le champ de texte.

Enfin, notez comment le constructeur ClockPanel dfinit la taille de prfrence :


public ClockPanel()
{
setPreferredSize(new Dimension(2 * RADIUS + 1, 2 * RADIUS + 1));
}

Lorsque la mthode pack du cadre calcule la taille du cadre, elle utilise la taille prfre du panneau.

Livre Java .book Page 407 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

Exemple 9.2 : TextTest.java


import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.awt.geom.*;
javax.swing.*;
javax.swing.event.*;

public class TextTest


{
public static void main(String[] args)
{
TextTestFrame frame = new TextTestFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec deux champs de texte pour dfinir lheure.
*/
class TextTestFrame extends JFrame
{
public TextTestFrame()
{
setTitle("TextTest");
DocumentListener listener = new ClockFieldListener();
// ajouter un panneau avec les champs de texte
JPanel panel = new JPanel();
panel.add(new JLabel("Hours:"));
hourField = new JTextField("12", 3);
panel.add(hourField);
hourField.getDocument().addDocumentListener(listener);
panel.add(new JLabel("Minutes:"));
minuteField = new JTextField("00", 3);
panel.add(minuteField);
minuteField.getDocument().addDocumentListener(listener);
add(panel, BorderLayout.SOUTH);
// ajouter lhorloge
clock = new ClockPanel();
add(clock, BorderLayout.CENTER);
pack();
}
/**
Affecte lhorloge les valeurs indiques
dans les champs de texte.
*/
public void setClock()
{
try

407

Livre Java .book Page 408 Jeudi, 25. novembre 2004 3:04 15

408

Au cur de Java 2 - Notions fondamentales

{
int hours
= Integer.parseInt(hourField.getText().trim());
int minutes
= Integer.parseInt(minuteField.getText().trim());
clock.setTime(hours, minutes);
}
catch (NumberFormatException e) {}
/* ne pas configurer lhorloge si les entres
saisies ne peuvent pas tre analyses */
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 300;
private JTextField hourField;
private JTextField minuteField;
private ClockPanel clock;
private class ClockFieldListener implements DocumentListener
{
public void insertUpdate(DocumentEvent event) { setClock(); }
public void removeUpdate(DocumentEvent event) { setClock(); }
public void changedUpdate(DocumentEvent event) {}
}
}
/**
Un panneau avec un dessin dhorloge.
*/
class ClockPanel extends JPanel
{
public ClockPanel()
{
setPreferredSize(new Dimension(2 * RADIUS + 1, 2 * RADIUS + 1));
}
public void paintComponent(Graphics g)
{
// dessiner le cadran
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
Ellipse2D circle
= new Ellipse2D.Double(0, 0, 2 * RADIUS, 2 * RADIUS);
g2.draw(circle);
// dessiner la grande aiguille
double hourAngle
= Math.toRadians(90 - 360 * minutes / (12 * 60));
drawHand(g2, hourAngle, HOUR_HAND_LENGTH);
// dessiner la petite aiguille
double minuteAngle
= Math.toRadians(90 - 360 * minutes / 60);
drawHand(g2, minuteAngle, MINUTE_HAND_LENGTH);
}

Livre Java .book Page 409 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

public void drawHand(Graphics2D g2,


double angle, double handLength)
{
Point2D end = new Point2D.Double(
RADIUS+handLength * Math.cos(angle),
RADIUS - handLength * Math.sin(angle));
Point2D center = new Point2D.Double(RADIUS, RADIUS);
g2.draw(new Line2D.Double(center, end));
}
/**
Dfinit lheure afficher sur lhorloge
@param h Heures
@param m Minutes
*/
public void setTime(int h, int m)
{
minutes = h * 60+m;
repaint();
}
private
private
private
private

double minutes = 0;
int RADIUS = 100;
double MINUTE_HAND_LENGTH = 0.8 * RADIUS;
double HOUR_HAND_LENGTH = 0.6 * RADIUS;

}
javax.swing.JComponent 1.2

void setPreferredSize(Dimension d)

Dfinit la taille prfre de ce composant.


javax.swing.text.Document 1.2

int getLength()

Renvoie le nombre de caractres actuellement dans le document.

String getText(int offset, int length)

Renvoie le texte contenu dans la partie de document indique.


Paramtres :

offset

Le dbut du texte.

length

La longueur de la chane dsire.

void addDocumentListener(DocumentListener listener)

Enregistre lcouteur qui doit tre notifi lorsque le document change.


javax.swing.event.DocumentEvent 1.2

Document getDocument()

Rcupre le document qui est la source de lvnement.


javax.swing.event.DocumentListener 1.2

void changedUpdate(DocumentEvent event)

Appele chaque fois quun attribut ou un ensemble dattributs est modifi.

409

Livre Java .book Page 410 Jeudi, 25. novembre 2004 3:04 15

410

Au cur de Java 2 - Notions fondamentales

void insertUpdate(DocumentEvent event)

Appele chaque fois quune insertion est effectue dans le document.

void removeUpdate(DocumentEvent event)

Appele chaque fois quune partie du document a t supprime.

Champs de mot de passe


Le champ de mot de passe est un type spcial de champ de texte. Pour viter que des voisins curieux
ne puissent apercevoir le mot de passe entr par un utilisateur, les caractres taps ne sont pas affichs. Un caractre dcho est utilis la place, gnralement un astrisque (*). La bibliothque
Swing fournit une classe JPasswordField qui implmente ce genre de champ.
Le champ de mot de passe est un autre exemple de la puissance de larchitecture Modle-VueContrleur. Il utilise le mme modle quun champ de texte standard pour conserver les donnes,
mais sa vue a t modifie pour nafficher que des caractres dcho.
javax.swing.JPasswordField 1.2
JPasswordField(String text, int columns)

Construit un nouveau champ de mot de passe.


Paramtres :

text

Le texte afficher ou la valeur null sil ny en a pas.

columns

Le nombre de colonnes.

void setEchoChar(char echo)

Dfinit le caractre dcho pour le champ de mot de passe. Un certain style dinterface peut
proposer son propre choix de caractres dcho. La valeur 0 rtablit le caractre dcho utilis
par dfaut.
Paramtres :

echo

Le caractre dcho afficher au lieu des caractres de texte.

char[] getPassword()

Renvoie le texte contenu dans le champ de mot de passe. Pour renforcer la scurit, vous devez
craser le contenu du tableau renvoy aprs utilisation. Le mot de passe nest pas retourn en tant
que String, car une chane resterait dans la machine virtuelle jusqu ce quelle soit limine
par le processus de nettoyage de mmoire (le ramasse-miettes ou garbage collector).

Champs de saisie mis en forme


Dans le dernier exemple, lutilisateur du programme devait taper des chiffres, et non des chanes
arbitraires. Lutilisateur nest autoris taper que des chiffres de 0 9 et un signe moins "". Si ce
signe est utilis, il doit reprsenter le premier caractre de la chane dentre. En apparence, la validation dentre semble simple. Nous pouvons mettre en uvre un couteur de touche pour le champ
de texte et bloquer (consume) tous les vnements des touches qui ne reprsentent pas un chiffre ou
un signe moins. Malheureusement, cette approche simple, bien que recommande comme mthode
de validation dentre, ne fonctionne pas bien dans la pratique. Tout dabord, certaines associations
de touches autorises ne constituent pas obligatoirement une entre valide, par exemple, --3 ou 3-3.
Plus important encore, il existe dautres moyens de modifier le texte qui ne font pas appel la pression dune touche. Selon le style dinterface implment, certaines combinaisons clavier peuvent
servir pour couper, copier ou coller du texte. Par exemple, dans le style Metal, la combinaison de

Livre Java .book Page 411 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

411

touches Ctrl+V colle le contenu du tampon dans le champ de texte. Pour cette raison, nous devrions
aussi nous assurer que lutilisateur ne colle pas de caractres invalides. Bref, cette tentative de filtrer
les frappes au clavier pour valider une entre commence devenir complexe. Cest un cas prcis
dlments dont un programmeur ne devrait pas avoir sinquiter.
Avant le JDK 1.4, il nexistait pas de composants permettant de saisir des valeurs numriques.
Depuis la premire dition de "Au cur de Java", nous avons propos une implmentation pour un
IntTextField, un champ de texte permettant de saisir un entier correctement mis en forme. Dans
chaque nouvelle dition, nous avons modifi limplmentation pour profiter de tout avantage, mme
limit, des divers schmas de validation ajouts chaque version du JDK. Enfin, dans le JDK 1.4, les
concepteurs Swing se sont attaqus au problme et ont fourni une classe JFormattedTextField qui
peut tre utilise non seulement pour la saisie de chiffres, mais galement pour les dates et pour des
mises en forme plus sotriques, comme les adresses IP.

Saisie dentiers
Commenons par un cas facile : un champ de texte pour la saisie dun entier :
JFormattedTextField intField = new
JFormattedTextField(NumberFormat.getIntegerInstance());

NumberFormat.getIntegerInstance renvoie un objet de mise en forme qui formate les entiers


laide des paramtres rgionaux. Dans les paramtres franais, les virgules servent de sparateurs
dcimaux, ce qui permet de saisir des valeurs comme 1,72. Le chapitre du Volume 2 sur linternationalisation explique en dtail comment slectionner dautres paramtres locaux.
Comme pour tout champ de texte, vous pouvez dfinir le nombre de colonnes :
intField.setColumns(6);

La mthode setValue peut tre accompagne dune valeur par dfaut. Elle prend un paramtre
Object, vous devrez donc envelopper la valeur int par dfaut dans un objet Integer:
intField.setValue(new Integer(100));

Les utilisateurs saisissent gnralement des informations dans plusieurs champs de texte, puis
cliquent sur un bouton pour lire toutes les valeurs. Aprs ce clic, vous pouvez rcuprer la valeur
fournie par lutilisateur grce la mthode getValue. Elle renvoie un rsultat Object et vous devez
la transtyper dans le type appropri. JFormattedTextField renvoie un objet du type Long si lutilisateur a modifi la valeur et lobjet Integer initial si ce nest pas le cas. Il vous faut donc transtyper
la valeur de retour sur la superclasse Number habituelle :
Number value = (Number) intField.getValue();
int v = value.intValue();

Le champ de texte mis en forme nest pas trs intressant tant que vous ne pensez pas ce qui
survient lorsque lutilisateur saisit des donnes non autorises. Cest le sujet de la prochaine section.

Comportement en cas de perte de focalisation


Imaginons un utilisateur entrant des donnes dans un champ de texte. Il tape des informations, puis
dcide finalement de quitter le champ, par exemple en cliquant sur un autre composant. Le champ de
texte perd alors le focus (la focalisation). Le curseur en I ny est plus visible et les frappes sur les
touches sont destines un autre composant.

Livre Java .book Page 412 Jeudi, 25. novembre 2004 3:04 15

412

Au cur de Java 2 - Notions fondamentales

Lorsque le champ de texte mis en forme perd le focus, llment de mise en forme tudie la chane
de texte produite par lutilisateur. Sil sait la convertir en objet, le texte est considr comme valide,
sinon il est signal non valide. Vous pouvez utiliser la mthode isEditValid pour vrifier la validit
du champ de texte.
Le comportement par dfaut en cas de perte de focus est appel "commit or revert" (engager ou
retourner). Si la chane de texte est valide, elle est engage (committed). Le formateur la transforme
en objet, qui devient la valeur actuelle du champ (cest--dire la valeur de retour de la mthode
getValue vue la section prcdente). La valeur est ensuite retransforme en chane, qui devient la
chane de texte visible dans le champ. Le formateur dentier reconnat par exemple que lentre 1729
est valide, il dfinit la valeur actuelle sur new Long(1729), puis la retransforme en chane en insrant
une espace pour les milliers : 1729.
A linverse, si la chane de texte nest pas valide, la valeur nest pas modifie et le champ de
texte retourne la chane reprsentant lancienne valeur. Par exemple, si lutilisateur entre une
valeur errone, comme x1, lancienne valeur est rcupre lorsque le champ de texte perd le
focus.
INFO
Le formateur dentier considre une chane de texte comme valide si elle commence par un entier. Par exemple,
1729x est une chane valide. Elle est transforme en 1729, puis mise en forme (1729).

La mthode setFocusLostBehavior permet de dfinir dautres comportements. Le comportement


"engager" (commit) est lgrement diffrent du comportement par dfaut. Si la chane de texte nest
pas valide, elle et la valeur du champ restent inchanges, elles sont dites hors synchronisation. Le
comportement "persister" (persist) est encore plus conservateur. Mme si la chane de texte est
valide, ni le champ de texte ni la valeur actuelle ne sont modifis. Il faut alors appeler commitEdit,
setValue ou setText pour les resynchroniser. Enfin, il existe un comportement dit "retourner"
(revert) qui ne semble avoir aucune utilit. Lors dune perte de focus, la saisie utilisateur est ignore
et la chane de texte retourne lancienne valeur.
INFO
Le comportement par dfaut "engager ou retourner" (commit or revert) convient gnralement bien. Il ne pose
quun problme potentiel. Supposons quune bote de dialogue contienne un champ de texte pour une valeur
dentier. Un utilisateur entre la chane " 1729", avec une espace pralable, puis clique sur OK. La premire
espace invalide le nombre, la valeur du champ retourne donc lancienne valeur. Lcouteur daction du bouton
OK rcupre la valeur du champ et ferme la bote de dialogue. Lutilisateur ne sait donc pas que la nouvelle
valeur a t refuse. Ici, le fait de choisir le comportement "engager" convient parfaitement, vous faites ensuite
vrifier par lcouteur du bouton OK que toutes les modifications du champ sont valides avant de fermer la bote
de dialogue.

Filtres
Cette fonction de base des champs de texte mis en forme est simple et suffit dans la plupart des
cas. Vous pouvez toutefois affiner quelque peu le processus. Il est possible, par exemple, dempcher lutilisateur dentrer dautres caractres que les chiffres. Pour ce faire, vous utiliserez un

Livre Java .book Page 413 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

413

filtre de document. Pour mmoire, dans larchitecture Modle-Vue-Contrleur, le contrleur


traduit les vnements de saisie en commandes qui modifient le document sous-jacent du champ
de texte, cest--dire la chane de texte stocke dans un objet PlainDocument. Par exemple, lorsque le contrleur traite une commande qui entrane linsertion de texte dans le document, il
appelle la commande "insert string". La chane insrer peut tre un caractre unique ou le
contenu du tampon. Un filtre de document interceptera cette commande et modifiera la chane ou
annulera linsertion. Voici le code de la mthode insertString dun filtre qui analyse la chane
insrer et ninsre que les caractres numriques ou le signe - (le code gre des caractres
Unicode complmentaires, ainsi quil est expliqu au Chapitre 3 ; voir Chapitre 12 pour la classe
StringBuilder) :
public void insertString(FilterBypass fb,
int offset, String string, AttributeSet attr)
throws BadLocationException
{
StringBuilder builder = new StringBuilder(string);
for (int i = builder.length() - 1; i >= 0; i--)
{
int cp = builder.codePointAt(i);
if (!Character.isDigit(cp) && cp!= -)
{
builder.deleteCharAt(i);
if (Character.isSupplementaryCodePoint(cp))
{
i--;
builder.deleteCharAt(i);
}
}
}
super.insertString(fb, offset, builder.toString(), attr);
}

Vous devez galement craser la mthode replace de la classe DocumentFilter: elle est appele
lorsque le texte est slectionn, puis remplac. Limplmentation de la mthode replace est simple
(voir le programme la fin de cette section).
Vous devez maintenant installer le filtre du document. Il nexiste malheureusement pas de mthode
simple pour le faire. Vous devez remplacer la mthode getDocumentFilter dune classe formatter, puis transmettre un objet de cette classe formatter au JFormattedTextField. Le champ de
texte dentier utilise un InternationalFormatter initialis avec NumberFormat.getIntegerInstance(). Ce code installe un formateur qui produira le filtre dsir :
JFormattedTextField intField = new JFormattedTextField(new
InternationalFormatter(NumberFormat.getIntegerInstance())
{
protected DocumentFilter getDocumentFilter()
{
return filter;
}
private DocumentFilter filter = new IntFilter();
});

Livre Java .book Page 414 Jeudi, 25. novembre 2004 3:04 15

414

Au cur de Java 2 - Notions fondamentales

INFO
La documentation du JDK prcise que la classe DocumentFilter a t invente pour viter le sous-classement.
Jusquau JDK 1.3, le filtrage dans un champ de texte sobtenait par lextension de la classe PlainDocument et le
remplacement des mthodes insertString et replace. Aujourdhui, la classe PlainDocument dispose dun filtre
modifiable dynamiquement (pluggable). Cest une trs bonne amlioration. Pourtant, il aurait encore mieux valu
que le filtre soit aussi modifiable dynamiquement dans la classe formatter. Ce nest hlas pas le cas, et nous devons
sous-classer le formateur.

Testez le programme dexemple FormatTest la fin de cette section. Un filtre est install pour le
troisime champ de texte. Vous ne pouvez insrer que des chiffres ou le caractre moins (-). Sachez
que vous pouvez toujours entrer des chanes non valides comme "1-2-3". En gnral, le filtrage ne
permet pas dviter toutes les chanes non valides. La chane "-", par exemple, nest pas valide mais
un filtre ne peut pas la refuser car cest le prfixe dune chane autorise comme "-1". Mme si les
filtres ne constituent pas une protection parfaite, leur utilisation est logique pour refuser des entres
qui apparaissent immdiatement non valides.
ASTUCE
Le filtrage permet aussi de transformer tous les caractres dune chane en majuscules. Ce filtre est simple crire.
Dans les mthodes insertString et replace du filtre, transformez la chane insrer en majuscules, puis appelez
la mthode superclass.

Vrificateurs
Il existe un autre mcanisme qui peut tre utile pour avertir les utilisateurs de saisies invalides. Il sagit
dun vrificateur qui sattache nimporte quel JComponent. Si le composant perd le focus, le vrificateur est appel. Lorsquil signale que le contenu du composant nest pas valide, le composant
reprend immdiatement le focus. Lutilisateur est alors oblig de corriger le contenu avant de
pouvoir saisir dautres informations.
Un vrificateur doit tendre la classe abstraite InputVerifier et dfinir une mthode verify. Il est
particulirement facile de dfinir un vrificateur qui vrifie les champs de texte mis en forme. La
mthode isEditValid de la classe JFormattedTextField appelle le formateur et renvoie true sil
peut transformer la chane de texte en objet.
Voici le vrificateur :
class FormattedTextFieldVerifier extends InputVerifier
{
public boolean verify(JComponent component)
{
JFormattedTextField field = (JFormattedTextField) component;
return field.isEditValid();
}
}

Vous pouvez lattacher tout JFormattedTextField:


intField.setInputVerifier(new FormattedTextFieldVerifier());

Livre Java .book Page 415 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

415

Toutefois, un vrificateur nest pas lpreuve de toutes les erreurs. Si vous cliquez sur un bouton, il
avertit ses couteurs daction avant quun composant non valide ne reprenne le focus. Les couteurs
daction peuvent alors rcuprer un rsultat non valide du composant dont la vrification a chou.
Et il existe une raison ce comportement : les utilisateurs peuvent vouloir appuyer sur le bouton
"Annuler" sans avoir corriger une entre non valide.
Un vrificateur est attach au quatrime champ de texte dans le programme dexemple. Essayez
dentrer un nombre non valide (comme x1729) et appuyez sur la touche de tabulation ou cliquez sur
un autre champ de texte. Sachez que le champ reprend immdiatement le focus. Toutefois, si vous
appuyez sur le bouton OK, lcouteur daction appelle getValue, qui signale la dernire valeur
correcte.

Autres formateurs standard


JFormattedTextField prend en charge dautres formateurs en plus du formateur dentier. La classe
NumberFormat possde les mthodes statiques
getNumberInstance
getCurrencyInstance
getPercentInstance

qui produisent respectivement les formateurs des nombres virgule flottante, des valeurs de devises
et des pourcentages. Vous pouvez par exemple obtenir un champ de texte pour la saisie des valeurs
de devise en appelant :
JFormattedTextField currencyField =
new JFormattedTextField(NumberFormat.getCurrencyInstance());

Pour modifier les dates et les heures, appelez lune des mthodes statiques de la classe DateFormat:
getDateInstance
getTimeInstance
getDateTimeInstance

Par exemple :
JFormattedTextField dateField = new
JFormattedTextField(DateFormat.getDateInstance());

Ce champ transforme une date au format "moyen" ou par dfaut comme :


Feb 24, 2002

Vous pouvez aussi choisir un format "court" comme (format amricain)


2/24/02

en appelant plutt
DateFormat.getDateInstance(DateFormat.SHORT)

INFO
Par dfaut, le format de date est assez "clment". Ainsi, une date non valide comme le 31 fvrier 2002 est transforme pour indiquer la prochaine date valide, savoir le 3 mars 2002. Attention, ce comportement peut surprendre les
utilisateurs ! Dans ce cas, appelez setLenient(false) sur lobjet DateFormat.

Livre Java .book Page 416 Jeudi, 25. novembre 2004 3:04 15

416

Au cur de Java 2 - Notions fondamentales

DefaultFormatter est capable de mettre en forme les objets de toute classe qui disposent dun
constructeur avec un paramtre de chane et une mthode toString correspondante. Par exemple, la
classe URL dispose dun constructeur URL(String) pouvant tre utilis pour construire une URL
depuis une chane, comme
URL url = new URL("http://java.sun.com");

Vous pouvez donc utiliser DefaultFormatter pour mettre en forme les objets URL. Le formateur
appelle toString sur la valeur du champ pour initialiser le texte. Lorsque le champ perd le focus, le
formateur construit un nouvel objet de la mme classe que la valeur actuelle, en utilisant le constructeur avec un paramtre String. Si ce constructeur dclenche une exception, la modification nest pas
valide. Vous pouvez tester cette situation dans le programme dexemple en entrant une URL qui ne
commence pas par un prfixe comme "http:".
INFO
Par dfaut, DefaultFormatter est en mode overwrite (mode de remplacement). Cette situation est diffrente
pour les autres formateurs et finalement pas trs utile. Appelez setOverwriteMode(false) pour dsactiver le
mode overwrite.

Enfin, MaskFormatter convient bien aux motifs taille fixe qui contiennent des caractres constants
et des caractres variables. Les numros de scurit sociale, par exemple (comme 1-56-08-75-205081-78), peuvent tre mis en forme de la manire suivante :
new MaskFormatter("#-##-##-##-###-###-##")

Le symbole # remplace un chiffre. Le Tableau 9.2 montre les symboles que vous pouvez utiliser
dans un formateur de masque.
Tableau 9.2 : Symboles de MaskFormatter

Un chiffre

Une lettre

Une lettre, transforme en majuscule

Une lettre, transforme en minuscule

Une lettre ou un chiffre

Un chiffre hexadcimal [0-9A-Fa-f]

Tout caractre

Caractre dchappement pour inclure un symbole dans le motif

Livre Java .book Page 417 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

417

Vous pouvez limiter les caractres pouvant tre taps dans le champ en appelant lune des mthodes
de la classe MaskFormatter:
setValidCharacters
setInvalidCharacters

Par exemple, pour lire une note scolaire exprime par une lettre (comme A+ ou F), vous pourriez
utiliser :
MaskFormatter formatter = new MaskFormatter("U*");
formatter.setValidCharacters("ABCDF+- ");

Il nexiste toutefois aucune mthode permettant dindiquer que le deuxime caractre ne doit pas
tre une lettre.
Sachez que la chane mise en forme par le formateur du masque a exactement la mme longueur que
le masque. Si lutilisateur efface des caractres pendant la modification, ceux-ci sont remplacs par
le caractre demplacement. Ce caractre est, par dfaut, une espace, mais vous pouvez le modifier
grce la mthode setPlaceholderCharacter, par exemple :
formatter.setPlaceholderCharacter(0);

Par dfaut, un formateur de masque agit en mode de recouvrement, un mode assez intuitif (testez le
programme dexemple). Sachez galement que la position du curseur passe au-dessus des caractres
fixes sur le masque.
Le formateur de masque est trs efficace pour les motifs rigides comme les numros de scurit
sociale ou les numros de tlphone. Sachez quaucune variation nest admise dans le motif du
masque. Vous ne pouvez pas, par exemple, utiliser un formateur de masque pour les numros de tlphone internationaux, dont le nombre de chiffres varie.

Formateurs personnaliss
Lorsque aucun des formateurs standard ne convient, vous pouvez assez facilement dfinir le vtre.
Envisagez des adresses IP 4 octets, comme
130.65.86.66

Vous ne pouvez pas utiliser un MaskFormatter car chaque octet pourrait tre reprsent par un, deux
ou trois chiffres. Il faut aussi sassurer que la valeur de chaque octet nexcde pas 255.
Pour personnaliser votre formateur, tendez la classe DefaultFormatter et remplacez les mthodes
String valueToString(Object value)
Object stringToValue(String text)

La premire mthode transforme la valeur du champ en chane qui saffiche dans le champ de texte.
La seconde analyse le texte tap par lutilisateur et le retransforme en objet. Si lune ou lautre
mthode dtecte une erreur, elle lance une exception ParseException.
Dans notre exemple de programme, nous stockons une adresse IP dans un tableau byte[] de
longueur 4. La mthode valueToString forme une chane qui spare les octets par des points.

Livre Java .book Page 418 Jeudi, 25. novembre 2004 3:04 15

418

Au cur de Java 2 - Notions fondamentales

Sachez que les valeurs doctet sont des quantits signes comprises entre -?128 et 127. Pour transformer des valeurs doctets ngatives en valeurs dentier non signes, ajoutez 256 :
public String valueToString(Object value) throws ParseException
{
if (!(value instanceof byte[]))
throw new ParseException("Not a byte[]", 0);
byte[] a = (byte[]) value;
if (a.length!= 4)
throw new ParseException("Length!= 4", 0);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 4; i++)
{
int b = a[i];
if (b < 0) b += 256;
builder.append(String.valueOf(b));
if (i < 3) builder.append(.);
}
return builder.toString();
}

A linverse, la mthode stringToValue analyse la chane et produit un objet byte[] si elle est
valide. Dans le cas contraire, elle dclenche une exception ParseException:
public Object stringToValue(String text) throws ParseException
{
StringTokenizer tokenizer = new StringTokenizer(text, ".");
byte[] a = new byte[4];
for (int i = 0; i < 4; i++)
{
int b = 0;
try
{
b = Integer.parseInt(tokenizer.nextToken());
}
catch (NumberFormatException e)
{
throw new ParseException("Not an integer", 0);
}
if (b < 0 || b >= 256)
throw new ParseException("Byte out of range", 0);
a[i] = (byte) b;
}
return a;
}

Testez le champ dadresse IP dans le programme dexemple. Si vous entrez une adresse non valide,
le champ retourne la dernire adresse valide.
Le programme de lexemple 9.3 montre les actions de divers champs de texte mis en forme (voir
Figure 9.13). Cliquez sur OK pour rcuprer les valeurs actuelles des champs.

Livre Java .book Page 419 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

419

INFO
La lettre dinformations en ligne "Swing Connection" contient un petit article qui dcrit un formateur correspondant
aux expressions ordinaires. Voir http://java.sun.com/products/jfc/tsc/articles/reftf/.

Figure 9.13
Le programme
FormatTest.

Exemple 9.3 : FormatTest.java


import
import
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.lang.reflect.*;
java.net.*;
java.text.*;
java.util.*;
javax.swing.*;
javax.swing.text.*;

/**
Un programme pour tester les champs de texte mis en forme
*/
public class FormatTest
{
public static void main(String[] args)
{
FormatTestFrame frame = new FormatTestFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec une srie de champs de texte
mis en forme et un bouton qui affiche les valeurs du champ.
*/
class FormatTestFrame extends JFrame
{
public FormatTestFrame()
{
setTitle("FormatTest");
setSize(WIDTH, HEIGHT);
JPanel buttonPanel = new JPanel();
okButton = new JButton("OK");
buttonPanel.add(okButton);
add(buttonPanel, BorderLayout.SOUTH);

Livre Java .book Page 420 Jeudi, 25. novembre 2004 3:04 15

420

Au cur de Java 2 - Notions fondamentales

mainPanel = new JPanel();


mainPanel.setLayout(new GridLayout(0, 3));
add(mainPanel, BorderLayout.CENTER);
JFormattedTextField intField = new
JFormattedTextField(NumberFormat.getIntegerInstance());
intField.setValue(new Integer(100));
addRow("Number:", intField);
JFormattedTextField intField2 = new
JFormattedTextField(NumberFormat.getIntegerInstance());
intField2.setValue(new Integer(100));
intField2.setFocusLostBehavior(JFormattedTextField.COMMIT);
addRow("Number (Commit behavior):", intField2);
JFormattedTextField intField3
= new JFormattedTextField(new
InternationalFormatter(NumberFormat.getIntegerInstance())
{
protected DocumentFilter getDocumentFilter()
{
return filter;
}
private DocumentFilter filter = new IntFilter();
});
intField3.setValue(new Integer(100));
addRow("Filtered Number", intField3);
JFormattedTextField intField4 = new
JFormattedTextField(NumberFormat.getIntegerInstance());
intField4.setValue(new Integer(100));
intField4.setInputVerifier(new FormattedTextFieldVerifier());
addRow("Verified Number:", intField4);
JFormattedTextField currencyField
= new JFormattedTextField(NumberFormat.getCurrencyInstance());
currencyField.setValue(new Double(10));
addRow("Currency:", currencyField);
JFormattedTextField dateField = new
JFormattedTextField(DateFormat.getDateInstance());
dateField.setValue(new Date());
addRow("Date (default):", dateField);
DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT);
format.setLenient(false);
JFormattedTextField dateField2 = new JFormattedTextField(format);
dateField2.setValue(new Date());
addRow("Date (short, not lenient):", dateField2);
try
{
DefaultFormatter formatter = new DefaultFormatter();
formatter.setOverwriteMode(false);
JFormattedTextField urlField = new
JFormattedTextField(formatter);
urlField.setValue(new URL("http://java.sun.com"));
addRow("URL:", urlField);
}

Livre Java .book Page 421 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

catch (MalformedURLException e)
{
e.printStackTrace();
}
try
{
MaskFormatter formatter = new MaskFormatter("###-##-####");
formatter.setPlaceholderCharacter(0);
JFormattedTextField ssnField = new
JFormattedTextField(formatter);
ssnField.setValue("078-05-1120");
addRow("SSN Mask:", ssnField);
}
catch (ParseException exception)
{
exception.printStackTrace();
}
JFormattedTextField ipField = new JFormattedTextField(new
IPAddressFormatter());
ipField.setValue(new byte[] { (byte) 130, 65, 86, 66 });
addRow("IP Address:", ipField);
}
/**
Ajoute une ligne au panneau principal.
@param labelText Le libell du champ
@param field Le champ dexemple
*/
public void addRow(String labelText, final JFormattedTextField field)
{
mainPanel.add(new JLabel(labelText));
mainPanel.add(field);
final JLabel valueLabel = new JLabel();
mainPanel.add(valueLabel);
okButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
Object value = field.getValue();
if (value.getClass().isArray())
{
StringBuilder builder = new StringBuilder();
builder.append({);
for (int i = 0; i < Array.getLength(value); i++)
{
if (i > 0) builder.append(,);
builder.append(Array.get(value, i).toString());
}
builder.append(});
valueLabel.setText(builder.toString());
}
else
valueLabel.setText(value.toString());
}
});
}

421

Livre Java .book Page 422 Jeudi, 25. novembre 2004 3:04 15

422

Au cur de Java 2 - Notions fondamentales

public static final int WIDTH = 500;


public static final int HEIGHT = 250;
private JButton okButton;
private JPanel mainPanel;
}
/**
Un filtre qui limite la saisie aux chiffres et au signe -.
*/
class IntFilter extends DocumentFilter
{
public void insertString(FilterBypass fb,
int offset, String string, AttributeSet attr)
throws BadLocationException
{
StringBuilder builder = new StringBuilder(string);
for (int i = builder.length() - 1; i >= 0; i--)
{
int cp = builder.codePointAt(i);
if (!Character.isDigit(cp) && cp!= -)
{
builder.deleteCharAt(i);
if (Character.isSupplementaryCodePoint(cp))
{
i--;
builder.deleteCharAt(i);
}
}
}
super.insertString(fb, offset, builder.toString(), attr);
}
public void replace(FilterBypass fb, int offset, int length,
String string, AttributeSet attr)
throws BadLocationException
{
if (string!= null)
{
StringBuilder builder = new StringBuilder(string);
for (int i = builder.length() - 1; i >= 0; i--)
{
int cp = builder.codePointAt(i);
if (!Character.isDigit(cp) && cp!= -)
{
builder.deleteCharAt(i);
if (Character.isSupplementaryCodePoint(cp))
{
i--;
builder.deleteCharAt(i);
}
}
}
string = builder.toString();
}

Livre Java .book Page 423 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

super.replace(fb, offset, length, string, attr);


}
}
/**
Un vrificateur qui vrifie si le contenu dun
champ de texte mis en forme est valide.
*/
class FormattedTextFieldVerifier extends InputVerifier
{
public boolean verify(JComponent component)
{
JFormattedTextField field = (JFormattedTextField) component;
return field.isEditValid();
}
}
/**
Un formateur pour les adresses IP 4 octets de la forme a.b.c.d
*/
class IPAddressFormatter extends DefaultFormatter
{
public String valueToString(Object value)
throws ParseException
{
if (!(value instanceof byte[]))
throw new ParseException("Not a byte[]", 0);
byte[] a = (byte[]) value;
if (a.length!= 4)
throw new ParseException("Length!= 4", 0);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 4; i++)
{
int b = a[i];
if (b < 0) b += 256;
builder.append(String.valueOf(b));
if (i < 3) builder.append(.);
}
return builder.toString();
}
public Object stringToValue(String text) throws ParseException
{
StringTokenizer tokenizer = new StringTokenizer(text, ".");
byte[] a = new byte[4];
for (int i = 0; i < 4; i++)
{
int b = 0;
if (!tokenizer.hasMoreTokens())
throw new ParseException("Too few bytes", 0);
try
{
b = Integer.parseInt(tokenizer.nextToken());
}
catch (NumberFormatException e)
{
throw new ParseException("Not an integer", 0);
}

423

Livre Java .book Page 424 Jeudi, 25. novembre 2004 3:04 15

424

Au cur de Java 2 - Notions fondamentales

if (b < 0 || b >= 256)


throw new ParseException("Byte out of range", 0);
a[i] = (byte) b;
}
if (tokenizer.hasMoreTokens())
throw new ParseException("Too many bytes", 0);
return a;
}
}
javax.swing.JFormattedTextField 1.4

JFormattedTextField(Format fmt)

Construit un champ de texte qui utilise le format spcifi.

JFormattedTextField(JFormattedTextField.AbstractFormatter formatter)

Construit un champ de texte utilisant le formateur spcifi. Sachez que DefaultFormatter


et InternationalFormatter sont des sous-classes de JFormattedTextField.AbstractFormatter.

Object getValue()

Renvoie la valeur valide courante du champ. Sachez que ceci peut ne pas correspondre la
chane modifie.

void setValue(Object value)

Tente de dfinir la valeur de lobjet donn. La tentative choue si le formateur ne peut pas
convertir lobjet en chane.

void commitEdit()

Tente de dfinir la valeur valide du champ partir de la chane modifie. La tentative peut
chouer si le formateur ne parvient pas convertir la chane.

boolean isEditValid()

Vrifie si la chane modifie reprsente une valeur valide.

void setFocusLostBehavior(int behavior)


int getFocusLostBehavior()

Dfinit ou rcupre le comportement "focus perdu". Les valeurs autorises pour le comportement sont les constantes COMMIT_OR_REVERT, REVERT, COMMIT et PERSIST de la classe JFormattedTextField.
java.text.DateFormat 1.1

static DateFormat getDateInstance()


static DateFormat getDateInstance(int dateStyle)
static DateFormat getTimeInstance()
static DateFormat getTimeInstance(int timeStyle)
static DateFormat getDateTimeInstance()
static DateFormat getDateTimeInstance(int dateStyle, int timeStyle)

Ces mthodes renvoient les formateurs qui produisent la date, lheure ou la date et lheure des
objets Date. Les valeurs autorises pour dateStyle et timeStyle sont les constantes SHORT,
MEDIUM, LONG, FULL et DEFAULT de la classe DateFormat.

Livre Java .book Page 425 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

425

javax.swing.JFormattedTextFieldAbstractFormatter 1.4

abstract String valueToString(Object value)

Transforme une valeur en chane modifiable. Dclenche une exception ParseException si la


valeur nest pas adapte ce formateur.

abstract Object stringToValue(String s)

Transforme une chane en valeur. Dclenche une exception ParseException si s na pas le


format appropri.

DocumentFilter getDocumentFilter()

Remplace cette mthode pour fournir un filtre de document qui limite les saisies dans le champ
de texte. Une valeur de retour null indique quaucun filtrage nest ncessaire.
javax.swing.text.DefaultFormatter 1.2

void setOverwriteMode(boolean mode)


boolean getOverwriteMode()

Dfinit ou rcupre le mode de remplacement. Si mode vaut true, de nouveaux caractres


remplacent les caractres existants lorsquils modifient le texte.
javax.swing.text.DocumentFilter 1.1

void insertString(DocumentFilter.FilterBypass bypass, int offset, String text,


AttributeSet attrib)

Cette mthode est appele avant linsertion dune chane dans un document. Vous pouvez
remplacer la mthode et modifier la chane. Vous pouvez dsactiver linsertion en nappelant pas
super.insertString ou en appelant les mthodes bypass pour modifier le document sans
filtrage.
Paramtres :

bypass

Un objet qui permet dexcuter des commandes de modification qui contournent le filtre.

offset

Le dcalage auquel insrer le texte.

text

Les caractres insrer.

attrib

Les attributs de mise en forme du texte insr.

void replace(DocumentFilter.FilterBypass bypass, int offset, int length, String


text, AttributeSet attrib)

Cette mthode est appele avant de remplacer une partie dun document par une nouvelle chane.
Vous pouvez remplacer la mthode et modifier la chane. Vous pouvez dsactiver le remplacement en nappelant pas super.replace ou en appelant les mthodes bypass pour modifier le
document sans filtrage.
Paramtres :

bypass

Un objet qui permet dexcuter des commandes de modification qui contournent le filtre.

offset

Le dcalage auquel insrer le texte.

length

La longueur de la partie remplacer.

text

Les caractres insrer.

attrib

Les attributs de mise en forme du texte insr.

Livre Java .book Page 426 Jeudi, 25. novembre 2004 3:04 15

426

Au cur de Java 2 - Notions fondamentales

void remove(DocumentFilter.FilterBypass bypass, int offset, int length)

Cette mthode est appele avant de supprimer une partie dun document. Rcupre le document
en appelant bypass.getDocument() pour analyser leffet de la suppression.
Paramtres :

bypass

Un objet qui permet dexcuter des commandes de modification qui contournent le filtre.

offset

Le dcalage de la partie supprimer.

length

La longueur de la partie supprimer.

javax.swing.text.MaskFormatter 1.4

MaskFormatter(String mask)

Construit un formateur de masque avec le masque donn. Voyez le Tableau 9.2 pour connatre les
symboles dun masque.

void setValidCharacters(String characters)


String getValidCharacters()

Dfinissent ou rcuprent les caractres de modification valides. Seuls les caractres de la chane
donne sont accepts pour les parties variables du masque.

void setInvalidCharacters(String characters)


String getInvalidCharacters()

Dfinissent ou rcuprent les caractres de modification non valides. Aucun des caractres de la
chane donne nest accept comme entre.

void setPlaceholderCharacter(char ch)


char getPlaceholderCharacter()

Dfinissent ou rcuprent le caractre demplacement utilis pour les caractres variables dans le
masque que lutilisateur na pas encore fourni. Le caractre demplacement par dfaut est une
espace.

void setPlaceholder(String s)
String getPlaceholder()

Dfinissent ou rcuprent la chane demplacement. Son extrmit est utilise si lutilisateur na


pas fourni tous les caractres variables dans le masque. Sil est null ou plus court que le
masque, le caractre demplacement permet de remplir les entres restantes.

void setValueContainsLiteralCharacters(boolean b)
boolean getValueContainsLiteralCharacters()

Dfinissent ou rcuprent la balise "la valeur contient les caractres littraux". Si cette
balise vaut true, la valeur du champ contient les parties littrales (non variables) du
masque. Si elle vaut false, les caractres littraux sont supprims. Le paramtre par dfaut
est true.

Zones de texte
Parfois, vous avez besoin de recueillir une entre dutilisateur dune longueur suprieure une
ligne. Pour mmoire, vous pouvez employer le composant JTextArea pour le faire. Lorsque
vous placez un composant de ce type dans votre programme, un utilisateur peut taper nimporte
quel nombre de lignes de texte en utilisant la touche Entre pour les sparer. Chaque ligne se

Livre Java .book Page 427 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

427

termine par un caractre retour de ligne \n. Si vous devez rpartir lentre de lutilisateur sur
plusieurs lignes, vous pouvez faire appel la classe StringTokenizer (voir Chapitre 12). La
Figure 9.12 montre comment se prsente une zone de texte.
Dans le constructeur du composant JTextArea, vous spcifiez le nombre de lignes et de colonnes
pour la zone. Par exemple :
textArea = new JTextArea(8, 40); // 8 lignes de 40 colonnes chacune

o le paramtre qui indique le nombre de colonnes fonctionne comme auparavant ; vous devez
toujours ajouter quelques colonnes (caractres) supplmentaires par prcaution. Lutilisateur
nest pas limit au nombre de lignes et de colonnes ; le texte dfilera si lentre est suprieure
aux valeurs spcifies. Vous pouvez galement modifier le nombre de colonnes et de lignes en
utilisant, respectivement, les mthodes setColumns et setRows. Les valeurs donnes nindiquent
quune prfrence, le gestionnaire de mise en forme peut toujours agrandir ou rduire la zone de
texte.
Figure 9.14
Une zone de texte.

Si le texte entr dpasse la capacit daffichage de la zone de texte, le reste du texte est coup. Pour
viter que des longues lignes ne soient tronques, vous pouvez activer le retour automatique la
ligne avec la mthode :
textArea.setLineWrap(true); // sauts de ligne automatiques

Ce renvoi la ligne nest quun effet visuel. Le texte dans le document nest pas modifi, aucun
caractre \n nest insr.
Dans Swing, une zone de texte ne dispose pas de barres de dfilement. Si vous souhaitez en ajouter,
prvoyez la zone de texte dans un panneau avec barres de dfilement JScrollPane.
textArea = new JTextArea(8, 40);
JScrollPane scrollPane = new JScrollPane(textArea);

Le panneau de dfilement gre ensuite la vue de la zone de texte. Des barres de dfilement apparaissent automatiquement si le texte entr dpasse la zone daffichage ; elles disparaissent si, lors dune
suppression, le texte restant tient dans la zone de texte. Le dfilement est gr en interne par le
panneau de dfilement votre programme na pas besoin de traiter les vnements de dfilement.

Livre Java .book Page 428 Jeudi, 25. novembre 2004 3:04 15

428

Au cur de Java 2 - Notions fondamentales

ASTUCE
Cest un mcanisme gnral que vous rencontrerez frquemment en travaillant avec Swing. Pour ajouter des barres
de dfilement un composant, placez-le lintrieur dun panneau de dfilement.

LExemple 9.4 donne le code complet pour le programme de dmonstration de la zone de texte.
Il vous permet simplement de taper et de modifier un texte. Cliquez sur le bouton "Insert" pour
insrer une phrase la fin du texte. Cliquez sur le second bouton pour activer ou dsactiver le
renvoi la ligne. Son nom bascule entre "Wrap" et "No wrap". Bien sr, vous pouvez simplement utiliser le clavier pour changer le texte dans la zone. Notez que vous pouvez slectionner
une portion du texte et raliser des oprations de couper, copier et coller laide des combinaisons Ctrl+X, Ctrl+C et Ctrl+V habituelles. Les raccourcis clavier sont spcifiques au style
dinterface. Ces combinaisons de touches particulires fonctionnent pour les look and feel
Metal et Windows.
INFO
Le composant JTextArea naffiche que du texte brut, sans polices spciales ni mise en forme. Pour afficher du
texte format tel que HTML ou RTF, servez-vous des classes JEditorPane et JTextPane, qui sont vues dans le
Volume 2.

Exemple 9.4 : TextAreaTest.java


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TextAreaTest
{
public static void main(String[] args)
{
TextAreaFrame frame = new TextAreaFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec une zone de texte et des boutons
pour la mise jour dun texte
*/
class TextAreaFrame extends JFrame
{
public TextAreaFrame()
{
setTitle("TextAreaTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
buttonPanel = new JPanel();

Livre Java .book Page 429 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

// un bouton pour ajouter du texte la fin de la zone


JButton insertButton = new JButton("Insert");
buttonPanel.add(insertButton);
insertButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
textArea.append("The quick brown fox
jumps over the lazy dog. ");
}
});
/* bouton pour activer/dsactiver le retour
automatique la ligne */
wrapButton = new JButton("Wrap");
buttonPanel.add(wrapButton);
wrapButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
boolean wrap =!textArea.getLineWrap();
textArea.setLineWrap(wrap);
scrollPane.revalidate();
wrapButton.setText(wrap? "No wrap": "Wrap");
}
});
add(buttonPanel, BorderLayout.SOUTH);
// ajouter une zone de texte avec dfilement
textArea = new JTextArea(8, 40);
scrollPane = new JScrollPane(textArea);
add(scrollPane, BorderLayout.CENTER);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 300;
private
private
private
private

JTextArea textArea;
JScrollPane scrollPane;
JPanel buttonPanel;
JButton wrapButton;

}
javax.swing.JTextArea 1.2

JTextArea(int rows, int cols)

Construit une nouvelle zone de texte.


Paramtres :

rows

Le nombre de lignes.

cols

Le nombre de colonnes.

429

Livre Java .book Page 430 Jeudi, 25. novembre 2004 3:04 15

430

Au cur de Java 2 - Notions fondamentales

JTextArea(String text, int rows, int cols)

Construit une nouvelle zone de texte avec un texte initial.


Paramtres :

text

Le texte initial.

rows

Le nombre de lignes.

cols

Le nombre de colonnes.

void setColumns(int cols)

Indique la zone de texte le nombre de colonnes prfr utiliser.


Paramtres :

cols

Le nombre de colonnes.

void setRows(int rows)

Indique la zone de texte le nombre de lignes prfr utiliser.


Paramtres :

rows

Le nombre de lignes.

void append(String newText)

Ajoute le texte spcifi la fin du texte dj prsent dans la zone de texte.


Paramtres :

newText

Le texte ajouter.

void setLineWrap(boolean wrap)

Active ou dsactive le retour automatique la ligne.


Paramtres :

wrap

true (vrai) si retour automatique des lignes longues.

void setWrapStyleWord(boolean word)

Si le paramtre word vaut true, les lignes longues sont scindes au niveau des fins de mot. Si la
valeur est false, elles sont divises sans tenir compte des fins de mot.

void setTabSize(int c)

Dfinit les marques de tabulation toutes les c colonnes. Notez que les tabulations ne sont pas
converties en espaces, mais provoquent lalignement avec la marque suivante.
Paramtres :

Le nombre de colonnes pour linsertion dune marque de


tabulation.

javax.swing.JScrollPane 1.2

JScrollPane(Component c)

Cre un panneau avec dfilement qui affiche le contenu du composant spcifi. Les barres de
dfilement apparaissent automatiquement lorsque le contenu du composant est plus grand que la
vue.
Paramtres :

Le composant faire dfiler.

Livre Java .book Page 431 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

431

Composants du choix
Vous savez maintenant comment recueillir du texte tap par les utilisateurs. Toutefois, il est souvent
prfrable de leur offrir un ensemble fini de choix plutt que de leur demander dintroduire des
donnes dans un composant de texte. Au moyen dun ensemble de boutons ou dune liste doptions,
vous pouvez indiquer aux utilisateurs les choix mis leur disposition. De plus, vous naurez pas
besoin de crer de procdures de contrle derreurs pour ces types de slections. Dans cette section,
vous apprendrez programmer des cases cocher, des boutons radio, des listes doptions et des
curseurs.

Cases cocher
Si vous souhaitez recueillir une rponse qui se limite une entre "oui" ou "non", utilisez une case
cocher. Ce type de composant saccompagne dun intitul qui permet de les identifier. Lutilisateur
clique lintrieur de la case pour la cocher, et fait de mme pour la dsactiver. Pour ajouter/supprimer la coche, lutilisateur peut galement appuyer sur la barre despacement si le focus dentre se
trouve dans la case cocher.
La Figure 9.15 illustre un programme simple avec deux cases cocher : lune pour activer ou dsactiver lattribut de mise en italique dune police et lautre pour lattribut de mise en gras. Notez que le
focus dentre se trouve sur la deuxime case doption, comme lindique le rectangle autour de son
intitul. Chaque fois que lutilisateur clique sur lune des options, lcran est mis jour avec les
nouveaux attributs de la police.
Figure 9.15
Exemples de cases
cocher.

Les cases cocher saccompagnent dun libell qui indique leur rle. Le texte prvu pour le libell
est pass au constructeur.
bold = new JCheckBox("Bold");

La mthode setSelected permet dactiver ou de dsactiver une case cocher, comme dans la ligne
suivante :
bold.setSelected(true);

La mthode isSelected extrait le statut actuel de chaque case cocher. Il est false si la case nest
pas coche, true le reste du temps.

Livre Java .book Page 432 Jeudi, 25. novembre 2004 3:04 15

432

Au cur de Java 2 - Notions fondamentales

Lorsque lutilisateur clique sur une case cocher, un vnement daction est dclench. Comme
toujours, vous associez un couteur daction la case cocher. Dans notre programme, les deux
cases partagent le mme couteur daction :
ActionListener listener = . . .
bold.addActionListener(listener);
italic.addActionListener(listener);

La mthode actionPerformed interroge le statut des cases cocher bold et italic et dfinit la
police du panneau Normal, Gras ou Italique, ou les deux :
public void actionPerformed(ActionEvent event)
{
int mode = 0;
if (bold.isSelected()) mode += Font.BOLD;
if (italic.isSelected()) mode += Font.ITALIC;
label.setFont(new Font("Serif", mode, FONTSIZE));
}

LExemple 9.5 prsente le programme complet dimplmentation des cases cocher.


Exemple 9.5 : CheckBoxtTest.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CheckBoxTest
{
public static void main(String[] args)
{
CheckBoxFrame frame = new CheckBoxFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un label pour lexemple de texte et des
cases cocher pour slectionner des attributs de police.
*/
class CheckBoxFrame extends JFrame
{
public CheckBoxFrame()
{
setTitle("CheckBoxTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter le label dexemple de texte
label = new JLabel("The quick brown fox jumps over the lazy dog.");
label.setFont(new Font("Serif", Font.PLAIN, FONTSIZE));
add(label, BorderLayout.CENTER);

Livre Java .book Page 433 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

// Cet couteur affecte lattribut de police du libell


// le statut de la case cocher
ActionListener listener = new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
int mode = 0;
if (bold.isSelected()) mode += Font.BOLD;
if (italic.isSelected()) mode += Font.ITALIC;
label.setFont(new Font("Serif", mode, FONTSIZE));
}
};
// ajouter les cases cocher
JPanel buttonPanel = new JPanel();
bold = new JCheckBox("Bold");
bold.addActionListener(listener);
buttonPanel.add(bold);
italic = new JCheckBox("Italic");
italic.addActionListener(listener);
buttonPanel.add(italic);
add(buttonPanel, BorderLayout.SOUTH);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
private JLabel label;
private JCheckBox bold;
private JCheckBox italic;
private static final int FONTSIZE = 12;
}
javax.swing.JCheckBox 1.2

JCheckBox(String label)

Construit une case cocher avec ltiquette donne qui est, lorigine, non slectionne.

JCheckBox(String label, boolean state)

Construit une case cocher avec ltiquette donne et ltat initial.

JCheckBox(String label, Icon icon)

Construit une case cocher avec ltiquette donne et une icne initialement dsactive.

boolean isSelected ()

Renvoie ltat de la case cocher.

void setSelected(boolean state)

Dfinit un nouvel tat pour la case cocher.

433

Livre Java .book Page 434 Jeudi, 25. novembre 2004 3:04 15

434

Au cur de Java 2 - Notions fondamentales

Boutons radio
Dans lexemple prcdent, lutilisateur pouvait activer ou dsactiver une ou plusieurs options, ou
aucune. De nombreuses situations exigent que lutilisateur ne puisse choisir quune seule option
parmi un ensemble de choix. Lorsquune seconde option est slectionne, la premire est dsactive.
Cet ensemble doptions est souvent mis en uvre au moyen dun groupe de boutons radio. Ils sont
ainsi appels, car ils fonctionnent de la mme manire que les boutons dune radio : lorsque vous
appuyez sur un bouton, celui qui tait enfonc ressort. La Figure 9.16 illustre un exemple typique.
Lutilisateur est autoris slectionner une taille de police parmi plusieurs possibilits : Petit,
Moyen, Grand, et Trs grand bien sr, il ne peut en choisir quune la fois.
Figure 9.16
Un groupe de boutons
radio.

Limplmentation des groupes de boutons radio est facile avec Swing. Construisez un objet de type
ButtonGroup pour chaque groupe de boutons. Ajoutez ensuite des objets du type JRadioButton au
groupe. Lobjet groupe est responsable de la dsactivation de la premire option choisie lors dune
seconde slection.
ButtonGroup group = new ButtonGroup();
JRadioButton smallButton
= new JRadioButton("Small", false);
group.add(smallButton);
JRadioButton mediumButton
= new JRadioButton("Medium", true);
group.add(mediumButton);
. . .

Passez la valeur true comme second argument au constructeur pour loption qui doit tre initialement slectionne et false pour les autres. Notez quun groupe de boutons ne contrle que le
comportement des boutons. Si vous voulez grouper les options pour des raisons de prsentation,
vous devez les placer dans un conteneur tel que JPanel.
Si vous observez de nouveau les Figures 9.15 et 9.16, vous noterez que les boutons radio nont pas
la mme apparence que les cases cocher. Ces dernires se prsentent sous la forme dun carr qui
contient une coche une fois quelles sont slectionnes. Les boutons radio sont ronds et contiennent
un point lorsquils sont activs.
Le mcanisme de notification dvnement est le mme pour les boutons radio que pour les autres
boutons. Lorsque lutilisateur active lun des boutons, il gnre un vnement daction. Dans notre

Livre Java .book Page 435 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

435

exemple de programme, nous dfinissons un couteur daction qui dfinit la taille de police une
valeur donne :
ActionListener listener = new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// size fait rfrence au paramtre final de
// la mthode addRadioButton
label.setFont(new Font("Serif", Font.PLAIN,
size));
}
};

Comparez cette configuration dcouteur celle de lexemple de case cocher. Chaque bouton radio
obtient un objet couteur diffrent. Chaque objet couteur sait exactement ce quil doit faire dfinir la taille de police une valeur particulire. Dans le cas des cases cocher, nous avons utilis
une approche diffrente. Les deux cases cocher avaient le mme couteur daction ; il appelait une
mthode qui examinait ltat des deux cases cocher.
Pourrions-nous adopter une approche similaire ici ? Nous pourrions avoir un seul couteur qui
calculerait la taille de la faon suivante :
if (smallButton.isSelected()) size = 8;
else if (mediumButton.isSelected()) size = 12;
. . .

Nous prfrons toutefois avoir recours des objets couteur daction diffrents, car ils associent les
valeurs de taille aux boutons avec une plus grande prcision.
INFO
Si vous utilisez un groupe de boutons radio, vous savez que seul lun dentre eux peut tre choisi. Il serait intressant
de pouvoir le localiser rapidement sans avoir interroger tous les boutons du groupe. Puisque lobjet ButtonGroup
contrle tous les boutons, il devrait pouvoir nous donner une rfrence celui qui a t slectionn. La classe
ButtonGroup possde bien une mthode getSelection, mais elle ne renvoie pas le bouton choisi. Elle retourne
une rfrence ButtonModel au modle associ au bouton. Malheureusement, aucune des mthodes de ButtonModel nest vraiment dun grand secours. Linterface ButtonModel hrite dune mthode getSelectedObjects
de linterface ItemSelectable qui, sans grande utilit, renvoie la valeur null. La mthode getActionCommand
semble prometteuse, car la "commande daction" dun bouton radio est son texte de libell. Mais la commande
daction de son modle vaut null. Ce nest que si vous dfinissez explicitement les commandes daction de tous les
boutons avec la mthode setActionCommand que les valeurs de commande daction des modles sont aussi dfinies.
Vous pouvez alors extraire la commande daction du bouton actuellement slectionn laide de :
buttonGroup.getSelection().getActionCommand()

LExemple 9.6 illustre le programme complet de slection de taille de police qui met en uvre un
ensemble de boutons radio.
Exemple 9.6 : RadioButtonTest.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

Livre Java .book Page 436 Jeudi, 25. novembre 2004 3:04 15

436

Au cur de Java 2 - Notions fondamentales

public class RadioButtonTest


{
public static void main(String[] args)
{
RadioButtonFrame frame = new RadioButtonFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}
/**
Un cadre avec un label pour lexemple de texte et des
boutons radio pour la slection de taille de police.
*/
class RadioButtonFrame extends JFrame
{
public RadioButtonFrame()
{
setTitle("RadioButtonTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter le libell dexemple de texte
label = new JLabel("The quick brown fox jumps over the lazy dog.");
label.setFont(new Font("Serif", Font.PLAIN,
DEFAULT_SIZE));
add(label, BorderLayout.CENTER);
// ajouter les boutons radio
buttonPanel = new JPanel();
group = new ButtonGroup();
addRadioButton("Small", 8);
addRadioButton("Medium", 12);
addRadioButton("Large", 18);
addRadioButton("Extra large", 36);
add(buttonPanel, BorderLayout.SOUTH);
}
/**
Ajoute un bouton radio qui dfinit la taille de police
de lexemple de texte.
@param name La chane de libell du bouton
@param size La taille de police que dfinit ce bouton
*/
public void addRadioButton(String name, final int size)
{
boolean selected = size == DEFAULT_SIZE;
JRadioButton button = new JRadioButton(name, selected);
group.add(button);
buttonPanel.add(button);
// Cet couteur dfinit la taille de police de lexemple
ActionListener listener = new
ActionListener()

Livre Java .book Page 437 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

437

{
public void actionPerformed(ActionEvent event)
{
// size fait rfrence au paramtre final de
// la mthode addRadioButton
label.setFont(new Font("Serif", Font.PLAIN,
size));
}
};
button.addActionListener(listener);
}
public static final int DEFAULT_WIDTH = 400;
public static final int DEFAULT_HEIGHT = 200;
private JPanel buttonPanel;
private ButtonGroup group;
private JLabel label;
private static final int DEFAULT_SIZE = 12;
}
javax.swing.JRadioButton 1.2

JRadioButton(String label, boolean state)

Construit un bouton radio avec le libell donn et ltat initial.

JRadioButton(String label, Icon icon)

Construit un bouton radio avec le libell donn et une icne qui est initialement dsactive..
javax.swing.ButtonGroup 1.2

void add(AbstractButton b)

Ajoute le bouton au groupe.

ButtonModel getSelection()

Renvoie le modle du bouton slectionn.


javax.swing.ButtonModel 1.2

String getActionCommand()

Renvoie la commande daction du modle du bouton.


javax.swing.AbstractButton 1.2

void setActionCommand(String s)

Dfinit la commande daction pour le bouton et son modle.

Bordures
Si vous prsentez plusieurs groupes de boutons radio dans une fentre, vous devrez indiquer visuellement quels sont ceux qui appartiennent au mme groupe. Swing fournit un ensemble de bordures
(ou cadres) qui sont utiles pour raliser cet effet. Vous pouvez appliquer une bordure nimporte
quel composant qui tend la classe JComponent. Lusage le plus courant est de placer une bordure
autour dun panneau et de le remplir avec dautres lments dinterface tels que des boutons radio.

Livre Java .book Page 438 Jeudi, 25. novembre 2004 3:04 15

438

Au cur de Java 2 - Notions fondamentales

Il y a plusieurs choix de bordures, mais leur emploi respecte la mme procdure :


1. Appelez une mthode static de BorderFactory pour crer une bordure. Vous pouvez choisir
parmi les styles suivants (voir Figure 9.17) :
LoweredBevel (relief incrust) ;
RaisedBevel (relief en saillie) ;
Etched (grave) ;
Line (trait uniforme) ;
Matte (avec trait variable ou texture) ;
Empty (vide ; pour crer un espace autour du composant).
2. Vous pouvez ajouter un titre votre bordure en la passant avec le titre la mthode BorderFactory.createTitledBorder.
3. Il est aussi possible de combiner plusieurs bordures avec la mthode BorderFactory.createCompoundBorder.
4. Ajoutez la bordure rsultante votre composant au moyen de la mthode setBorder de la classe
JComponent.
Voici, par exemple, de quelle faon ajouter une bordure grave avec un titre dans un panneau :
Border etched = BorderFactory.createEtchedBorder()
Border titled = BorderFactory.createTitledBorder(etched,
"A Title");
panel.setBorder(titled);

Excutez le programme de lExemple 9.7 pour avoir un aperu de lapparence des diverses bordures
proposes.
Les bordures saccompagnent doptions qui permettent den dfinir lpaisseur et la couleur. Reportez-vous la documentation API pour plus de renseignements ce sujet. Les fans de bordures apprcieront la classe SoftBevelBorder qui permet de crer une bordure avec des angles arrondis ; une
bordure LineBorder peut aussi avoir des coins arrondis. Toutefois, leur cration nest possible quau
moyen dun des constructeurs de la classe ; il nexiste pas de mthode BorderFactory pour ces
types.
Figure 9.17
Test des types
de bordures.

Exemple 9.7 : BorderTest.java


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

Livre Java .book Page 439 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

import javax.swing.*;
import javax.swing.border.*;
public class BorderTest
{
public static void main(String[] args)
{
BorderFrame frame = new BorderFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec des boutons radio pour choisir un style de bordure.
*/
class BorderFrame extends JFrame
{
public BorderFrame()
{
setTitle("BorderTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
demoPanel = new JPanel();
buttonPanel = new JPanel();
group = new ButtonGroup();
addRadioButton("Lowered bevel",
BorderFactory.createLoweredBevelBorder());
addRadioButton("Raised bevel",
BorderFactory.createRaisedBevelBorder());
addRadioButton("Etched",
BorderFactory.createEtchedBorder());
addRadioButton("Line",
BorderFactory.createLineBorder(Color.BLUE));
addRadioButton("Matte",
BorderFactory.createMatteBorder(
10, 10, 10, 10, Color.BLUE));
addRadioButton("Empty",
BorderFactory.createEmptyBorder());
Border etched = BorderFactory.createEtchedBorder();
Border titled = BorderFactory.createTitledBorder
(etched, "Border types");
buttonPanel.setBorder(titled);
setLayout(new GridLayout(2, 1));
add(buttonPanel);
add(demoPanel);
}
public void addRadioButton(String buttonName, final Border b)
{
JRadioButton button = new JRadioButton(buttonName);
button.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{

439

Livre Java .book Page 440 Jeudi, 25. novembre 2004 3:04 15

440

Au cur de Java 2 - Notions fondamentales

demoPanel.setBorder(b);
validate();
}
});
group.add(button);
buttonPanel.add(button);
}
public static final int DEFAULT_WIDTH = 600;
public static final int DEFAULT_HEIGHT = 200;
private JPanel demoPanel;
private JPanel buttonPanel;
private ButtonGroup group;
}
javax.swing.BorderFactory 1.2

static Border createLineBorder(Color color)


static Border createLineBorder(Color color, int thickness)

Crent une bordure avec un trait simple rgulier.

static MatteBorder createMatteBorder(int top, int left, int bottom, int right,
Color color)

static MatteBorder createMatteBorder(int top, int left, int bottom, int right,
Icon tileIcon)

Crent une bordure paisse remplie avec une couleur ou une rptition dicnes (mosaque).

static Border createEmptyBorder()


static Border createEmptyBorder(int top, int left, int bottom, int right)

Crent une bordure vide.

static Border createEtchedBorder()


static Border createEtchedBorder(Color highlight, Color shadow)
static Border createEtchedBorder(int type)
static Border createEtchedBorder(int type, Color highlight, Color shadow)

Crent une bordure avec un effet 3D.


Paramtres :

lumire, ombre

Couleurs pour un effet 3D.

type

Lun des types EtchedBorder.RAISED ou


EtchedBorder.LOWERED.

static Border createBevelBorder(int type)


static Border createBevelBorder(int type, Color light, Color shadow)
static Border createLoweredBevelBorder()
static Border createRaisedBevelBorder()

Crent une bordure qui donne leffet dune surface avec un relief incrust ou en saillie.
Paramtres :

type
highlight,
shadow

Lun des types BevelBorder.LOWERED ou


BevelBorder.RAISED
Couleurs pour un effet 3D.

Livre Java .book Page 441 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

441

static TitledBorder createTitledBorder(String title)

static TitledBorder createTitledBorder(Border border, String title, int justification, int position, Font font)

static TitledBorder createTitledBorder(Border border, String title, int justification, int position, Font font, Color color)

static TitledBorder createTitledBorder(Border border)


static TitledBorder createTitledBorder(Border border, String title)
static TitledBorder createTitledBorder(Border border, String title, int justification, int position)

Crent une bordure de titre avec les proprits spcifies.


Paramtres :

title

La chane pour le titre.

border

La bordure laquelle associer le titre.

justification

Lune des constantes TitleBorder.LEFT,


TitleBorder.CENTER, et TitleBorder.RIGHT.

position

Lune des constantes ABOVE_TOP, TOP, B


ELOW_TOP, ABOVE_BOTTOM, BOTTOM et BELOW_BOTTOM.

font

La police du titre.

color

La couleur du titre.

static CompoundBorder createCompoundBorder(Border outsideBorder, Border insideBorder)

Combine deux bordures pour en crer une nouvelle.


javax.swing.border.SoftBevelBorder 1.2

SoftBevelBorder(int type)
SoftBevelBorder(int type, Color highlight, Color shadow)

Crent une bordure en relief avec des angles arrondis.


Paramtres :

type
highlight,
shadow

Lun des types BevelBorder.LOWERED ou


BevelBorder.RAISED.
Couleurs pour un effet 3D.

javax.swing.border.LineBorder 1.2

public LineBorder(Colorcolor, intthickness, booleanroundedCorners)

Cre une ligne de bordure avec lpaisseur et la couleur indiques. Si roundedCorners vaut
true, les coins sont arrondis.
javax.swing.JComponent 1.2

void setBorder(Border border)

Dfinit la bordure pour le composant.

Livre Java .book Page 442 Jeudi, 25. novembre 2004 3:04 15

442

Au cur de Java 2 - Notions fondamentales

Listes droulantes
Ds que le nombre doptions augmente, les boutons radio ne conviennent plus, car ils occupent trop
despace lcran. Vous pouvez, dans ce cas, utiliser une liste droulante. Lorsque lutilisateur
clique sur le champ, une liste de choix se droule ; il peut ainsi faire une slection (voir Figure 9.18).
Figure 9.18
Une liste droulante.

Si la liste droulante est configure pour accepter une saisie (editable), il est alors possible de changer le choix actuel en tapant dans le champ, comme pour un champ de texte. La liste droulante est
aussi appele liste combine ; elle allie la souplesse dun champ de texte et une liste de choix prdfinis. La classe JComboBox implmente ce composant.
Vous appelez la mthode setEditable pour que la liste puisse tre modifiable. Notez que la saisie
ne modifie que llment courant et ne change pas le contenu de la liste.
Vous pouvez obtenir la slection courante ou le texte tap en appelant la mthode getSelectedItem.
Dans notre exemple de programme, lutilisateur peut choisir un style de police partir dune liste
(Serif, SansSerif, Monospaced, etc.). Il peut aussi taper une autre police dans le champ de slection.
Vous ajoutez les options avec la mthode addItem. Dans notre programme, elle nest appele que
dans le constructeur, mais vous pouvez lappeler tout moment :
faceCombo = new JComboBox();
faceCombo.setEditable(true);
faceCombo.addItem("Serif");
faceCombo.addItem("SansSerif");
. . .

Cette mthode ajoute la chane la fin de la liste. Vous pouvez ajouter de nouvelles options
nimporte quel endroit dans la liste avec la mthode insertItemAt:
faceCombo.insertItemAt("Monospaced", 0); // ajoute loption au dbut

Vous pouvez ajouter des lments de tout type la liste droulante invoque la mthode toString
de chaque lment pour lafficher.

Livre Java .book Page 443 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

443

Si vous devez supprimer des options au cours de lexcution, utilisez la mthode removeItem ou
removeItemAt, selon que vous spcifiez llment ou sa position :
faceCombo.removeItem("Monospaced");
faceCombo.removeItemAt(0); // supprimer le premier lment

Il existe aussi une mthode removeAllItems qui supprime toutes les options de la liste.
ASTUCE
Si vous devez ajouter un grand nombre dlments une liste droulante, la mthode addItem fonctionnera mal.
Construisez plutt un DefaultComboBoxModel, remplissez-le en appelant addElement, puis appelez la mthode
setModel de la classe JComboBox.

Lorsque lutilisateur slectionne une option de la liste droulante, un vnement daction est gnr.
Pour trouver llment slectionn, appelez getSource sur le paramtre event (vnement) pour
obtenir une rfrence la liste qui a envoy lvnement. Appelez ensuite la mthode getSelectedItem pour rcuprer loption slectionne. Vous devez convertir la valeur renvoye vers le type
appropri, gnralement une chane (String).
public void actionPerformed(ActionEvent event)
{
label.setFont(new Font(
(String) faceCombo.getSelectedItem() ,
Font.PLAIN,
DEFAULT_SIZE));
}

LExemple 9.8 dcrit le programme complet.


INFO
Pour afficher une liste en permanence au lieu dutiliser une liste droulante, faites appel au composant JList. Nous
traiterons de JList dans le Volume 2.

Exemple 9.8 : ComboBoxTest.java


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ComboBoxTest
{
public static void main(String[] args)
{
ComboBoxFrame frame = new ComboBoxFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un label pour lexemple de texte et une zone de liste
droulante pour slectionner les types de police.

Livre Java .book Page 444 Jeudi, 25. novembre 2004 3:04 15

444

Au cur de Java 2 - Notions fondamentales

*/
class ComboBoxFrame extends JFrame
{
public ComboBoxFrame()
{
setTitle("ComboBoxTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter le libell dexemple de texte
label = new JLabel("The quick brown fox jumps over the lazy dog.");
label.setFont(new Font("Serif", Font.PLAIN,
DEFAULT_SIZE));
add(label, BorderLayout.CENTER);
// crer une liste droulante et ajouter les types de polices
faceCombo = new JComboBox();
faceCombo.setEditable(true);
faceCombo.addItem("Serif");
faceCombo.addItem("SansSerif");
faceCombo.addItem("Monospaced");
faceCombo.addItem("Dialog");
faceCombo.addItem("DialogInput");
// Lcouteur de la liste droulante remplace la police du
// libell contenant lexemple par celle slectionne
faceCombo.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
label.setFont(new Font(
(String)faceCombo.getSelectedItem(),
Font.PLAIN,
DEFAULT_SIZE));
}
});
// ajouter une liste droulante un panneau dans la
// partie sud du cadre
JPanel comboPanel = new JPanel();
comboPanel.add(faceCombo);
add(comboPanel, BorderLayout.SOUTH);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
private JComboBox faceCombo;
private JLabel label;
private static final int DEFAULT_SIZE = 12;
}

Livre Java .book Page 445 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

445

javax.swing.JComboBox 1.2

void setEditable(boolean b)

Paramtres :

true si le champ de la liste droulante est modifiable par


lutilisateur, false sinon.

void addItem(Object item)

Ajoute un lment dans la liste.

void insertItemAt(Object item, int index)

Insre un lment dans la liste la position donne par lindice.

void removeItem(Object item)

Supprime un lment de la liste.

void removeItemAt(int index)

Supprime llment dans la liste la position donne par lindice.

void removeAllItems()

Supprime tous les lments de la liste.

Object getSelectedItem()

Renvoie llment actuellement slectionn.

Curseurs
Les zones de liste droulantes permettent lutilisateur de choisir parmi un ensemble de valeurs. Les
curseurs proposent un choix exhaustif de valeurs, par exemple, nimporte quel nombre compris entre
1 et 100.
La mthode la plus courante pour construire un curseur est la suivante :
JSlider curseur = new JSlider(min, max, valeurInitiale);

Si vous omettez les valeurs minimum, maximum et initiale, elles sont respectivement initialises 0,
100 et 50.
Vous pouvez positionner le curseur verticalement, laide de lappel suivant au constructeur :
JSlider curseur = new JSlider(SwingConstants.VERTICAL,
min, max, valeurInitiale);

Ces constructeurs crent un curseur normal, comme celui se trouvant en haut de la Figure 9.19. Nous
allons vous indiquer comment y ajouter des fioritures.
Au fur et mesure que lutilisateur fait glisser le curseur, la valeur volue entre les valeurs minimum
et maximum. Un vnement ChangeEvent est alors envoy tous les couteurs de changement.
Pour obtenir une notification du changement, vous devez appeler la mthode addChangeListener et
installer un objet qui implmente linterface ChangeListener. Cette interface possde une seule
mthode, stateChanged, de laquelle vous devez extraire la valeur du curseur :
public void stateChanged(ChangeEvent event)
{
JSlider slider = (JSlider)event.getSource();

Livre Java .book Page 446 Jeudi, 25. novembre 2004 3:04 15

446

Au cur de Java 2 - Notions fondamentales

int value = slider.getValue();


. . .
}

Figure 9.19
Diffrents types
de curseurs.

Vous pouvez amliorer le curseur en affichant des repres. Par exemple, dans notre programme
exemple, le deuxime curseur utilise la configuration suivante :
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);

Le curseur affiche des grands repres toutes les 20 units et des petits repres toutes les 5 units. Les
repres font rfrence aux valeurs du curseur, et non des pixels.
Les instructions ci-dessus ne font que dfinir les units pour les repres. Pour les afficher, vous devez
appeler :
slider.setPaintTicks(true);

Les petites et grandes marques de repres sont indpendantes. Vous pouvez parfaitement dfinir les
grandes marques toutes les 20 units et les petites toutes les 7 units, mais vous risquez dobtenir
une chelle assez droutante.
Vous pouvez forcer le curseur saligner sur les repres. Chaque fois que lutilisateur relche le
curseur dans ce mode de dplacement, il est positionn automatiquement sur le repre la plus
proche. Vous activez ce mode laide de lappel :
slider.setSnapToTicks(true);

INFO
Le mode dalignement sur les repres ne fonctionne pas aussi bien que prvu. Tant que le curseur nest pas rellement align, lcouteur de changement fait toujours tat de valeurs qui ne correspondent pas des repres. Et si
vous cliquez ensuite sur le curseur une action qui est cense faire bouger le curseur de la valeur dun repre le
curseur ne se dplace pas vers le repre suivant (ou prcdent).

Livre Java .book Page 447 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

447

Vous pouvez demander laffichage de valeurs de repres pour les grandes marques, en appelant :
slider.setPaintLabels(true);

Par exemple, pour un curseur allant de 0 100 ayant des grandes marques espaces de 20, les repres
seront libells 0, 20, 40, 60, 80 et 100.
Vous pouvez aussi fournir dautres libells, comme des chanes ou des icnes (voir Figure 9.19). Le
processus est un peu compliqu. Vous devez remplir une table de hachage avec des cls de type
Integer et des valeurs de type Component (lautoboxing simplifie ceci partir de la version 5.0
du JDK). Vous appelez ensuite la mthode setLabelTable. Les composants sont placs sous les
marques de repres. Vous avez gnralement recours des objets JLabel. Voici comment libeller
les marques avec A, B, C, D, E et F :
Hashtable<Integer, Component> labelTable =
new Hashtable<Integer, Component>();
labelTable.put(0, new JLabel("A"));
labelTable.put(20, new JLabel("B"));
. . .
labelTable.put(100, new JLabel("F"));
slider.setLabelTable(labelTable);

Reportez-vous au Chapitre 2 du Volume 2 pour plus dinformations au sujet des tables de hachage.
LExemple 9.9 montre aussi comment programmer un curseur avec des icnes comme libells de
repres.
ASTUCE
Si vos marques ou libells de repres ne saffichent pas, vrifiez que vous avez bien appel setPaintTicks(true)
et setPaintLabels(true).

Pour supprimer le "couloir" dans lequel se dplace le curseur, appelez


slider.setPaintTrack(false);

Le quatrime curseur de la Figure 9.19 ne prsente pas de couloir.


Le remplissage du cinquime curseur est invers par lappel suivant :
slider.setInverted(true);

Le programme dexemple montre comment obtenir tous ces effets visuels sur une srie de curseurs.
Un couteur dvnement de changement est install pour chaque curseur ; il place la valeur actuelle
du curseur dans le champ de texte en bas du cadre.
Exemple 9.9 : SliderTest.java
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;
javax.swing.event.*;

Livre Java .book Page 448 Jeudi, 25. novembre 2004 3:04 15

448

Au cur de Java 2 - Notions fondamentales

public class SliderTest


{
public static void main(String[] args)
{
SliderTestFrame frame = new SliderTestFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec une srie de curseurs et une zone de texte pour
afficher les valeurs du curseur.
*/
class SliderTestFrame extends JFrame
{
public SliderTestFrame()
{
setTitle("SliderTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
sliderPanel = new JPanel();
sliderPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
// couteur commun tous les curseurs
listener = new
ChangeListener()
{
public void stateChanged(ChangeEvent event)
{
// mettre jour le champ de texte lorsque la
// valeur du curseur change
JSlider source = (JSlider) event.getSource();
textField.setText(""+source.getValue());
}
};
// ajouter un curseur normal
JSlider slider = new JSlider();
addSlider(slider, "Normal");
// ajouter un curseur avec des repres grands et petits
slider = new JSlider();
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
addSlider(slider, "Ticks");
// ajouter un curseur qui saligne sur les repres
slider = new JSlider();
slider.setPaintTicks(true);
slider.setSnapToTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
addSlider(slider, "Snap to ticks");

Livre Java .book Page 449 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

// ajouter un curseur sans couloir


slider = new JSlider();
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
slider.setPaintTrack(false);
addSlider(slider, "No track");
// ajouter un curseur invers
slider = new JSlider();
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
slider.setInverted(true);
addSlider(slider, "Inverted");
// ajouter un curseur avec des libells numriques
slider = new JSlider();
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
addSlider(slider, "Labels");
// ajouter un curseur avec libells alphabtiques
slider = new JSlider();
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
Dictionary<Integer, Component> labelTable =
new Hashtable<Integer, Component>();
labelTable.put(0, new JLabel("A"));
labelTable.put(20, new JLabel("B"));
labelTable.put(40, new JLabel("C"));
labelTable.put(60, new JLabel("D"));
labelTable.put(80, new JLabel("E"));
labelTable.put(100, new JLabel("F"));
slider.setLabelTable(labelTable);
addSlider(slider, "Custom labels");
// ajouter un curseur avec des icnes comme libells
slider = new JSlider();
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.setSnapToTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(20);
labelTable = new Hashtable<Integer, Component>();

449

Livre Java .book Page 450 Jeudi, 25. novembre 2004 3:04 15

450

Au cur de Java 2 - Notions fondamentales

// ajouts les images de cartes


labelTable.put(0,
new JLabel(new ImageIcon("nine.gif")));
labelTable.put(20,
new JLabel(new ImageIcon("ten.gif")));
labelTable.put(40,
new JLabel(new ImageIcon("jack.gif")));
labelTable.put(60,
new JLabel(new ImageIcon("queen.gif")));
labelTable.put(80,
new JLabel(new ImageIcon("king.gif")));
labelTable.put(100,
new JLabel(new ImageIcon("ace.gif")));
slider.setLabelTable(labelTable);
addSlider(slider, "Icon labels");
// ajouter le champ de texte affichant la valeur du curseur
textField = new JTextField();
add(sliderPanel, BorderLayout.CENTER);
add(textField, BorderLayout.SOUTH);
}
/**
Ajoute un curseur au panneau curseur et attache lcouteur
@param s Le curseur
@param description La description du curseur
*/
public void addSlider(JSlider s, String description)
{
s.addChangeListener(listener);
JPanel panel = new JPanel();
panel.add(s);
panel.add(new JLabel(description));
sliderPanel.add(panel);
}
public static final int DEFAULT_WIDTH = 350;
public static final int DEFAULT_HEIGHT = 450;
private JPanel sliderPanel;
private JTextField textField;
private ChangeListener listener;
}
javax.swing.JSlider 1.2

JSlider()
JSlider(int direction)

Livre Java .book Page 451 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

451

JSlider(int min, int max)


JSlider(int min, int max, int initialValue)
JSlider(int direction, int min, int max, int initialValue)

Construisent un curseur horizontal avec la direction et les valeurs minimum, maximum et initiale
donnes.
Paramtres :

direction

Lune des valeurs SwingConstants.VERTICAL ou


SwingConstants.HORIZONTAL. Par dfaut horizontal.

min, max

Mini et maxi pour les valeurs de curseurs. Par dfaut 0


et 100.

InitialValue

Valeur initiale du curseur. Par dfaut 50.

void setPaintTicks(boolean b)

Affiche des repres si b vaut true.

void setMajorTickSpacing(int units)


void setMinorTickSpacing(int units)

Dfinissent les repres grands et petits comme multiples des units de curseur donnes.

void setPaintLabels(boolean b)

Si b vaut true, les libells des repres sont affichs.

void.setTable(Dictionary table)

Dfinit les composants utiliser comme libells des repres. Chaque paire cl/valeur dans la
table a la forme new Integer(valeur)/composant.

void setSnapToTicks(boolean b)

Si b vaut true, le curseur saligne sur le repre le plus proche aprs chaque dplacement.

void setPaintTrack(boolean b)

Si b vaut true, le curseur se dplace dans un couloir.

Le composant JSpinner
Un JSpinner est un champ de texte disposant de deux petits boutons. Lorsque lutilisateur clique
dessus, la valeur du champ de texte est augmente ou diminue (voir Figure 9.20).
Figure 9.20
Variations du composant
Jspinner.

Livre Java .book Page 452 Jeudi, 25. novembre 2004 3:04 15

452

Au cur de Java 2 - Notions fondamentales

Les valeurs du champ flch peuvent tre des nombres, des dates, des valeurs dune liste ou, plus
gnralement, une suite de valeurs dans laquelle il est possible de naviguer. La classe JSpinner dfinit des modles de donnes standard pour les trois premiers cas. Vous pouvez dfinir votre propre
modle de donnes et ainsi obtenir des suites arbitraires.
Par dfaut, un champ flch gre un entier que les boutons augmentent ou diminuent de 1. Vous
pouvez rcuprer la valeur actuelle en appelant la mthode getValue. Cette mthode renvoie un
Object. Transtypez-le en un Integer et rcuprez la valeur enveloppe :
JSpinner defaultSpinner = new JSpinner();
. . .
int value = (Integer) defaultSpinner.getValue();

Vous pouvez modifier lincrmentation pour quelle soit diffrente de 1 et fournir des limites infrieures et suprieures. Voici un champ flch commenant 5 dont les valeurs sont comprises entre
0 et 10 et augmentent par incrmentations de 0,5 :
JSpinner boundedSpinner = new JSpinner(
new SpinnerNumberModel(5, 0, 10, 0.5));

Il existe deux constructeurs SpinnerNumberModel, lun avec le seul paramtre int et lautre avec
des paramtres double. Si lun des paramtres est un nombre virgule flottante, on utilise le
deuxime constructeur : il dfinit la valeur du bouton flch sur un objet Double.
Les boutons flchs ne sont pas limits des valeurs numriques. Vous pouvez amener un bouton
flch faire dfiler toute suite de valeurs. Transfrez simplement un SpinnerListModel au
constructeur JSpinner. Pour construire un SpinnerListModel, utilisez un tableau ou une classe
implmentant linterface List (comme un ArrayList). Dans notre exemple, nous affichons un
bouton flch faisant dfiler tous les noms de police disponibles :
String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().
getAvailableFontFamilyNames();
JSpinner listSpinner = new JSpinner(new SpinnerListModel(fonts));

Nous avons toutefois considr que la direction de litration tait assez peu claire ; cest en effet
loppos de ce que lon a lhabitude de voir dans une liste droulante. Les valeurs suprieures viennent gnralement aprs les valeurs infrieures, la flche descendante permettant alors de naviguer
vers les valeurs suprieures. Mais le bouton flch augmente lindice du tableau de sorte que la
flche ascendante produise des valeurs suprieures. Il nexiste pas de manire dinverser lordre du
SpinnerListModel; toutefois, une sous-classe anonyme produit le rsultat souhait :
JSpinner reverseListSpinner = new JSpinner(
new SpinnerListModel(fonts)
{
public Object getNextValue()
{
return super.getPreviousValue();
}
public Object getPreviousValue()
{
return super.getNextValue();
}
});

Testez les deux versions et voyez celle qui vous parat la plus intuitive.

Livre Java .book Page 453 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

453

Les boutons flchs peuvent aussi tre utiles pour augmenter ou diminuer une date. Vous obtenez un
bouton flch initialis avec la date du jour grce lappel
JSpinner dateSpinner = new JSpinner(new SpinnerDateModel());

Cependant, si vous tudiez soigneusement la Figure 9.20, vous verrez que le texte du bouton flch
montre la fois la date et lheure, par exemple
3/12/02 7:23 PM

Lheure est inutile dans un choix de date. Il se rvle pourtant assez difficile de crer ce bouton
naffichant que la date. Voici lincantation magique :
JSpinner betterDateSpinner = new JSpinner(new SpinnerDateModel());
String pattern = ((SimpleDateFormat)
DateFormat.getDateInstance()).toPattern();
betterDateSpinner.setEditor(new JSpinner.DateEditor(betterDateSpinner,
pattern));

Ce procd permet aussi de crer un systme pour connatre lheure. Utilisez le constructeur SpinnerDateModel pour spcifier une Date, les limites infrieures et suprieures (ou null sil nexiste
pas de limite) et le champ Calendar (comme Calendar.HOUR) modifier :
JSpinner timeSpinner = new JSpinner(
new SpinnerDateModel(
new GregorianCalendar(2000, Calendar.JANUARY, 1, 12, 0,
0).getTime(),
null, null, Calendar.HOUR));

Toutefois, si vous souhaitez mettre jour les minutes par incrments de 15, vous allez au-del des
capacits de la classe SpinnerDateModel standard.
Vous pouvez afficher des suites arbitraires dans un champ flch en dfinissant votre propre modle.
Dans notre exemple de programme, nous disposons dun champ flch qui procde une itration
sur toutes les permutations de la chane "meat" (viande, en anglais). Vous pouvez accder "mate",
"meta", "team" et vingt autres permutations en cliquant sur les boutons.
Lorsque vous dfinissez votre propre modle, tendez la classe AbstractSpinnerModel et dfinissez
les quatre mthodes suivantes :
Object getValue()
void setValue(Object value)
Object getNextValue()
Object getPreviousValue()

La mthode getValue renvoie la valeur stocke par le modle. La mthode setValue dfinit une
nouvelle valeur. Elle lancera une exception IllegalArgumentException si la nouvelle valeur ne
convient pas.
ATTENTION
La mthode setValue doit appeler la mthode fireStateChanged aprs avoir dfini la nouvelle valeur, faute de
quoi le champ flch ne sera pas mis jour.

Livre Java .book Page 454 Jeudi, 25. novembre 2004 3:04 15

454

Au cur de Java 2 - Notions fondamentales

Les mthodes getNextValue et getPreviousValue renvoient les valeurs qui doivent venir aprs ou
avant la valeur courante ou null si la fin de litration est atteinte.
ATTENTION
Les valeurs getNextValue et getPreviousValue ne doivent pas modifier la valeur courante. Cliquer sur la flche
ascendante du bouton appelle la mthode getNextValue. Si la valeur de retour nest pas null, elle est dfinie par
un appel setValue.

Dans le programme dexemple, nous utilisons un algorithme standard pour dterminer les permutations
suivantes et prcdentes. Ne vous proccupez pas des dtails de lalgorithme.
LExemple 9.10 montre comment gnrer les divers types de boutons flchs. Cliquez sur le bouton
OK pour voir les valeurs du bouton.
Exemple 9.10 : SpinnerTest.java
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.text.*;
java.util.*;
javax.swing.*;

/**
Un programme qui teste les champs flchs.
*/
public class SpinnerTest
{
public static void main(String[] args)
{
SpinnerFrame frame = new SpinnerFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau contenant plusieurs champs flchs
et un bouton qui affiche les valeurs du champ.
*/
class SpinnerFrame extends JFrame
{
public SpinnerFrame()
{
setTitle("SpinnerTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
JPanel buttonPanel = new JPanel();
okButton = new JButton("Ok");
buttonPanel.add(okButton);
add(buttonPanel, BorderLayout.SOUTH);
mainPanel = new JPanel();
mainPanel.setLayout(new GridLayout(0, 3));
add(mainPanel, BorderLayout.CENTER);

Livre Java .book Page 455 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

455

JSpinner defaultSpinner = new JSpinner();


addRow("Default", defaultSpinner);
JSpinner boundedSpinner = new JSpinner(
new SpinnerNumberModel(5, 0, 10, 0.5));
addRow("Bounded", boundedSpinner);
String[] fonts = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames();
JSpinner listSpinner = new JSpinner(new SpinnerListModel(fonts));
addRow("List", listSpinner);
JSpinner reverseListSpinner = new JSpinner(
new
SpinnerListModel(fonts)
{
public Object getNextValue()
{
return super.getPreviousValue();
}
public Object getPreviousValue()
{
return super.getNextValue();
}
});
addRow("Reverse List", reverseListSpinner);
JSpinner dateSpinner = new JSpinner(new SpinnerDateModel());
addRow("Date", dateSpinner);
JSpinner betterDateSpinner = new JSpinner(new SpinnerDateModel());
String pattern = ((SimpleDateFormat) DateFormat.getDateInstance()).
toPattern();
betterDateSpinner.setEditor(new JSpinner.DateEditor(betterDateSpinner,
pattern));
addRow("Better Date", betterDateSpinner);
JSpinner timeSpinner = new JSpinner(
new SpinnerDateModel(
new GregorianCalendar(2000, Calendar.JANUARY, 1, 12, 0,
0).getTime(),
null, null, Calendar.HOUR));
addRow("Time", timeSpinner);
JSpinner permSpinner = new JSpinner(new PermutationSpinnerModel
("meat"));
addRow("Word permutations", permSpinner);
}
/**
Ajoute une ligne au panneau principal.
@param labelText Le libell du champ flch
@param spinner Lexemple de champ flch

Livre Java .book Page 456 Jeudi, 25. novembre 2004 3:04 15

456

Au cur de Java 2 - Notions fondamentales

*/
public void addRow(String labelText, final JSpinner spinner)
{
mainPanel.add(new JLabel(labelText));
mainPanel.add(spinner);
final JLabel valueLabel = new JLabel();
mainPanel.add(valueLabel);
okButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
Object value = spinner.getValue();
valueLabel.setText(value.toString());
}
});
}
public static final int DEFAULT_WIDTH = 400;
public static final int DEFAULT_HEIGHT = 250;
private JPanel mainPanel;
private JButton okButton;
}
/**
Un modle qui gnre dynamiquement des permutations de mots
*/
class PermutationSpinnerModel extends AbstractSpinnerModel
{
/**
Construit le modle.
@param w Le mot permuter
*/
public PermutationSpinnerModel(String w)
{
word = w;
}
public Object getValue()
{
return word;
}
public void setValue(Object value)
{
if (!(value instanceof String))
throw new IllegalArgumentException();
word = (String) value;
fireStateChanged();
}
public Object getNextValue()
{
int[] codePoints = toCodePointArray(word);
for (int i = codePoints.length - 1; i > 0; i--)
{
if (codePoints[i - 1] < codePoints[i])

Livre Java .book Page 457 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

{
int j = codePoints.length - 1;
while (codePoints[i - 1] > codePoints[j]) j--;
swap(codePoints, i - 1, j);
reverse(codePoints, i, codePoints.length - 1);
return new String(codePoints, 0, codePoints.length);
}
}
reverse(codePoints, 0, codePoints.length - 1);
return new String(codePoints, 0, codePoints.length);
}
public Object getPreviousValue()
{
int[] codePoints = toCodePointArray(word);
for (int i = codePoints.length - 1; i > 0; i--)
{
if (codePoints[i - 1] > codePoints[i])
{
int j = codePoints.length - 1;
while (codePoints[i - 1] < codePoints[j]) j--;
swap(codePoints, i - 1, j);
reverse(codePoints, i, codePoints.length - 1);
return new String(codePoints, 0, codePoints.length);
}
}
reverse(codePoints, 0, codePoints.length - 1);
return new String(codePoints, 0, codePoints.length);
}
private static int[] toCodePointArray(String str)
{
int[] codePoints = new int[str.codePointCount(0, str.length())];
for (int i = 0, j = 0; i < str.length(); i++, j++)
{
int cp = str.codePointAt(i);
if (Character.isSupplementaryCodePoint(cp)) i++;
codePoints[j] = cp;
}
return codePoints;
}
private static void swap(int[] a, int i, int j)
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
private static void reverse(int[] a, int i, int j)
{
while (i < j) { swap(a, i, j); i++; j--; }
}
private String word;
}

457

Livre Java .book Page 458 Jeudi, 25. novembre 2004 3:04 15

458

Au cur de Java 2 - Notions fondamentales

javax.swing.JSpinner 1.4

JSpinner()

Construit un champ flch qui modifie un entier avec une valeur de dpart de 0, une incrmentation
de 1 et pas de limite.

JSpinner(SpinnerModel model)

Construit un champ flch qui utilise le modle de donnes indiqu.

Object getValue()

Rcupre la valeur actuelle du champ flch.

void setValue(Object value)

Tente de dfinir la valeur du champ flch. Dclenche une exception IllegalArgumentException


si le modle naccepte pas la valeur.

void setEditor(JComponent editor)

Dfinit le composant utilis pour modifier la valeur du bouton flch.


javax.swing.SpinnerNumberModel 1.4

SpinnerNumberModel(int initval, int minimum, int maximum, int stepSize)


SpinnerNumberModel(double initval, double minimum, double maximum, double stepSize)

Ces constructeurs produisent des modles de nombre qui grent une valeur Integer ou Double.
Utilisez les constantes MIN_VALUE et MAX_VALUE des classes Integer et Double pour les valeurs
sans limites.
Paramtres :

initval

La valeur initiale.

minimum

La valeur valide minimale.

maximum

La valeur valide maximale.

stepSize

Lincrmentation ou la dcrmentation de chaque intervalle.

javax.swing.SpinnerListModel 1.4

SpinnerListModel(Object[] values)
SpinnerListModel(List values)

Ces constructeurs produisent des modles qui slectionnent une valeur parmi les valeurs
donnes.
javax.swing.SpinnerDateModel 1.4

SpinnerDateModel()

Construit un modle de date avec la date du jour comme valeur initiale, pas de limite infrieure
ou suprieure et une incrmentation de Calendar.DAY_OF_MONTH.

SpinnerDateModel(Date initval, Comparable minimum, Comparable


step)

Paramtres :

maximum, int

initval

La valeur initiale.

minimum

La valeur valide minimale ou null si aucune limite


infrieure nest souhaite.

Livre Java .book Page 459 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

459

maximum

La valeur valide maximale ou null si aucune limite


suprieure nest souhaite.

step

Le champ de date incrmenter ou dcrmenter de chaque


intervalle. Lune des constantes ERA, YEAR, MONTH,
WEEK_OF_YEAR, WEEK_OF_MONTH, DAY_OF_MONTH,
DAY_OF_YEAR, DAY_OF_WEEK, DAY_OF_WEEK_IN_MONTH,
AM_PM, HOUR, HOUR_OF_DAY, MINUTE, SECOND ou
MILLISECOND de la classe Calendar.

java.text.SimpleDateFormat 1.1

String toPattern() 1.2

Rcupre le motif de modification pour ce formateur de date. On utilise souvent "jj-MMAAAA". Voyez la documentation JDK pour en savoir plus sur le motif.
javax.swing.JSpinner.DateEditor 1.4

DateEditor(JSpinner spinner, String pattern)

Construit un diteur de date pour un champ flch.


Paramtres :

spinner

Le champ flch auquel appartient cet diteur.

pattern

Le motif du format pour le SimpleDateFormat associ.

javax.swing.AbstractSpinnerModel 1.4

Object getValue()

Rcupre la valeur actuelle du modle.

void setValue(Object value)

Tente de dfinir une nouvelle valeur pour le modle. Lance une exception IllegalArgumentException si la valeur nest pas acceptable. Lors du remplacement de cette mthode, vous devez
appeler fireStateChanged aprs avoir dfini la nouvelle valeur.

Object getNextValue()
Object getPreviousValue()

Ces mthodes calculent (mais ne dfinissent pas) la valeur suivante ou prcdente dans la suite
dfinie par ce modle.

Menus
Ce chapitre a dbut par une introduction aux composants les plus connus pouvant tre placs dans
une fentre : les diffrents types de boutons, les champs de texte et les listes droulantes. Swing gre
aussi un autre type dlment dinterface, les menus droulants, utiliss largement dans les interfaces
GUI.

Livre Java .book Page 460 Jeudi, 25. novembre 2004 3:04 15

460

Au cur de Java 2 - Notions fondamentales

Une barre de menus situe au sommet de la fentre contient les noms des menus droulants. Il suffit
de cliquer dessus pour les ouvrir, ce qui fait apparatre des options de menu et de sous-menus. Lorsque lutilisateur clique sur une option de menu, tous les menus sont ferms et un message est envoy
au programme. La Figure 9.21 prsente un menu classique comprenant un sous-menu.
Figure 9.21
Un menu contenant
un sous-menu.

Cration dun menu


La cration de menus est une opration assez simple. On commence par crer une barre de menus :
JMenuBar menuBar = new JMenuBar();

Une barre de menus est un simple composant que vous pouvez ajouter nimporte o. Le plus
souvent, elle est place au sommet dun cadre. Pour cela, la mthode setJMenuBar est appele :
frame.setJMenuBar(menuBar);

Pour chaque menu, vous devez crer un objet menu :


JMenu editMenu = new JMenu("Edit");

Vous ajoutez les menus principaux la barre de menus :


menuBar.add(editMenu);

Vous ajoutez ensuite lobjet menu les options du menu, les sparateurs et les sous-menus :
JMenuItem pasteItem = new JMenuItem("Paste");
editMenu.add(pasteItem);
editMenu.addSeparator();
JMenu optionsMenu = . . .; // un sous-menu
editMenu.add(optionsMenu);

Vous pouvez voir les sparateurs la Figure 9.21 sous les lments "Paste" et "Read-only".
Lorsque lutilisateur slectionne un menu, un vnement daction est dclench. Vous devez installer
un couteur daction pour chaque option de menu :
ActionListener listener = . . .;
pasteItem.addActionListener(listener);

Il existe une mthode, JMenu.add(String s), qui ajoute une option la fin dun menu, par exemple :
editMenu.add("Paste");

Livre Java .book Page 461 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

461

La mthode add renvoie loption de menu cre, afin que vous puissiez la capturer et lui associer un
couteur, de la faon suivante :
JMenuItem pasteItem = editMenu.add("Paste");
pasteItem.addActionListener(listener);

Il arrive frquemment que des options de menu lancent des commandes qui peuvent aussi tre actives par le biais dautres lments de linterface utilisateur, comme les boutons de la barre doutils.
Au Chapitre 8, vous avez vu comment spcifier des commandes par lintermdiaire des objets
Action. Vous dfinissez une classe qui implmente linterface Action, gnralement par drivation
de la classe AbstractAction. Vous spcifiez le libell de loption de menu dans le constructeur de
lobjet AbstractAction, puis vous surchargez la mthode actionPerformed pour quelle
contienne le gestionnaire daction du menu. Par exemple :
Action exitAction = new
AbstractAction("Exit") // texte de loption de menu ici
{
public void actionPerformed(ActionEvent event)
{
// code de laction ici
System.exit(0);
}
};

Vous pouvez alors ajouter laction au menu :


JMenuItem exitItem = fileMenu.add(exitAction);

Cette commande ajoute une option au menu, laide du nom de laction. Lobjet action devient son
couteur. Cest un raccourci pratique pour :
JMenuItem exitItem = new JMenuItem(exitAction);
fileMenu.add(exitItem);

INFO
Sous Windows et Macintosh, les menus sont gnralement dfinis dans un fichier de ressources externes, qui est li
aux applications par lintermdiaire des identifiants de ressources. Il est possible de crer des menus par programmation, mais cela arrive rarement. Dans Java, les menus sont habituellement conus au sein des programmes, car le
mcanisme de gestion des ressources externes est beaucoup plus limit que ceux de Windows ou de Mac OS.

javax.swing.JMenu 1.2

JMenu(String label)

Construit un menu.
Paramtres :

label

Lintitul du menu dans la barre de menus ou le menu parent.

JMenuItem add(JMenuItem option)

Ajoute une option de menu (ou un menu).


Paramtres :

option

Loption ou le menu ajouter.

JMenuItem add(String label)

Ajoute une option au menu.


Paramtres :

label

Lintitul de loption de menu.

Livre Java .book Page 462 Jeudi, 25. novembre 2004 3:04 15

462

Au cur de Java 2 - Notions fondamentales

JMenuItem add(Action a)

Ajoute une option de menu et lui associe une action.


Paramtres :

Une action encapsulant un nom, une icne optionnelle et un


couteur (voir Chapitre 8).

void addSeparator()

Ajoute une ligne de sparation dans le menu.

JMenuItem insert(JMenuItem menu, int index)

Ajoute une nouvelle option (ou sous-menu) dans le menu la position donne par lindice.
Paramtres :

menu

Le menu ajouter.

index

Emplacement o ajouter loption.

JMenuItem insert(Action a, int index)

Ajoute une nouvelle option au menu la position donne par lindice et y associe une action.
Paramtres :

Une action encapsulant un nom, une icne facultative et un


couteur.

index

Emplacement o ajouter loption.

void insertSeparator(int index)

Ajoute un sparateur la position donne par lindice.


Paramtres :

index

Emplacement o ajouter le sparateur.

void remove(int index)

Supprime une option spcifique du menu la position donne par lindice.


Paramtres :

index

La position de loption supprimer.

void remove(JMenuItem option)

Supprime une option spcifique du menu.


Paramtres :

option

Loption supprimer.

javax.swing.JMenuItem 1.2

JMenuItem(String label)

Construit un lment de menu avec un intitul donn.

JMenuItem(Action a) 1.3

Construit un lment de menu pour laction donne.


Paramtres :

Une action encapsulant un nom, une icne facultative et un


couteur.

javax.swing.AbstractButton 1.2

void setAction(Action a) 1.3

Dfinit laction pour ce bouton ou lment de menu.


Paramtres :

Une action encapsulant un nom, une icne facultative


et un couteur.

Livre Java .book Page 463 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

463

javax.swing.JFrame 1.2

void setJMenuBar(JMenuBar menubar)

Dfinit la barre de menus de la fentre.

Icnes et options de menu


Les options de menu sont trs semblables aux boutons. En ralit, la classe JMenuItem tend la
classe AbstractButton. Comme les boutons, les menus peuvent contenir un libell, une icne, ou
les deux. Vous pouvez spcifier une icne laide des constructeurs JMenuItem (String, Icon)
ou JMenuItem (Icon), ou la dfinir laide de la mthode setIcon dont la classe JMenuItem hrite
de la classe AbstractButton. Par exemple :
JMenuItem cutItem = new JMenuItem("Cut", new ImageIcon("cut.gif"));

La Figure 9.22 prsente un menu dont certaines options comprennent des icnes. Le texte des
options est plac par dfaut droite de licne. Si vous prfrez que le texte soit plac gauche,
appelez la mthode setHorizontalTextPosition dont la classe JMenuItem hrite de la classe
AbstractButton. Par exemple, lappel
cutItem.setHorizontalTextPosition(SwingConstants.LEFT);

dplace le texte de loption de menu gauche de licne.


Vous pouvez aussi ajouter une icne une action :
cutAction.putValue(Action.SMALL_ICON, new ImageIcon("cut.gif"));

Lorsque vous construisez une option de menu partir dune action, la valeur Action.NAME devient
le texte de loption de menu, et la valeur Action.SMALL_ICON devient licne.
Figure 9.22
Icnes associes
des options de menu.

Vous pouvez aussi dfinir licne dans le constructeur de AbstractAction:


cutAction = new
AbstractAction("Cut", new ImageIcon("cut.gif"))
{
public void actionPerformed(ActionEvent event)
{
// le code de laction ici
}
};

Livre Java .book Page 464 Jeudi, 25. novembre 2004 3:04 15

464

Au cur de Java 2 - Notions fondamentales

javax.swing.JMenuItem 1.2

JMenuItem(String label, Icon icon)

Construit un lment de menu avec lintitul donn et une icne.


javax.swing.AbstractButton 1.2

void setHorizontalTextPosition(int position)

Dfinit la position horizontale du texte par rapport licne.


Paramtres :
ou

pos

SwingConstants.RIGHT (texte plac droite de licne),


SwingConstants.LEFT (texte plac gauche).

javax.swing.AbstractAction 1.2

AbstractAction(String name, Icon smallIcon)

Construit une action abstraite avec le nom donn et une icne.

Options de menu avec cases cocher et boutons radio


Il est possible dajouter des cases cocher ou des boutons radio aux options de menu (voir
Figure 9.23). Lorsque lutilisateur slectionne loption, elle passe ltat activ ou dsactiv, selon
son tat initial.
Figure 9.23
Une option de menu
coche et des options
avec boutons radio.

<

Except leur aspect, vous traitez ces options de la mme manire que toutes les autres. Voici, par
exemple, comment crer une option avec une case cocher :
JCheckBoxMenuItem readonlyItem
= new JCheckBoxMenuItem("Read-only");
optionsMenu.add(readonlyItem);

Les boutons radio des options de menu fonctionnent de faon standard. Vous devez les inclure dans
un groupe de boutons. Quand lun de ces boutons est choisi, tous les autres sont automatiquement
dsactivs :
ButtonGroup group = new ButtonGroup();
JRadioButtonMenuItem insertItem
= new JRadioButtonMenuItem("Insert");

Livre Java .book Page 465 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

465

insertItem.setSelected(true);
JRadioButtonMenuItem overtypeItem = new JRadioButtonMenuItem("Overtype");
group.add(insertItem);
group.add(overtypeItem);
optionsMenu.add(insertItem);
optionsMenu.add(overtypeItem);

Lorsque vous utilisez ce type doption, vous navez gnralement pas besoin dtre inform du
moment exact o lutilisateur effectue sa slection. Vous pouvez simplement employer la mthode
isSelected pour tester ltat actuel dune option (cela implique bien sr que vous conserviez une
rfrence sur loption de menu stocke dans un champ dinstance). Utilisez la mthode setSelected
pour dfinir ltat dune option.
javax.swing.JCheckBoxMenuItem 1.2

JCheckBoxMenuItem(String label)

Cre une option de menu avec case cocher et lintitul spcifi.

JCheckBoxMenuItem(String label, boolean state)

Cre une option de menu avec case cocher avec lintitul et ltat initial spcifis (true signifie
coche).
javax.swing.JRadioButtonMenuItem 1.2

JRadioButtonMenuItem(String label)

Cre une option de menu avec bouton radio et lintitul spcifi.

JRadioButtonMenuItem(String label, boolean state)

Cre une option de menu avec bouton radio avec lintitul et ltat initial spcifis (true signifie
active).
javax.swing.AbstractButton 1.2

boolean isSelected()

Renvoie ltat dune option (true signifie active).

void setSelected(boolean state)

Dfinit ltat dune option.

Menus contextuels
Un menu contextuel est un menu qui nest pas attach une barre de menus, mais qui flotte lintrieur
dune fentre (voir Figure 9.24).
Figure 9.24
Un menu contextuel.

Livre Java .book Page 466 Jeudi, 25. novembre 2004 3:04 15

466

Au cur de Java 2 - Notions fondamentales

Vous crez un menu contextuel comme nimporte quel autre menu, sachant quil ne possde pas de
titre :
JPopupMenu popup = new JPopupMenu();

Vous ajoutez ensuite les options comme laccoutume :


JMenuItem item = new JMenuItem("Cut");
item.addActionListener(listener);
popup.add(item);

Contrairement la barre de menus standard qui apparat toujours au sommet dun cadre, un menu
contextuel doit tre explicitement affich laide de la mthode show. Vous devez spcifier le
composant parent et la position du menu contextuel en fonction du systme de coordonnes du
parent. Par exemple :
popup.show(panel, x, y);

Le code est habituellement crit pour que le menu surgisse au moment o lutilisateur clique sur un
bouton particulier de la souris. Sous Windows et Linux, il sagit souvent du bouton droit. Pour faire
apparatre un menu lorsque lutilisateur clique sur un composant, appelez simplement la mthode :
component.setComponentPopupMenu(popup);

Trs occasionnellement, vous pouvez placer un composant dans un autre qui dispose dun menu
contextuel. Le composant enfant peut hriter du menu contextuel du composant parent en appelant
child.setInheritsPopupMenu(true);

Ces mthodes ont t ajoutes au JDK 5.0 pour isoler les programmeurs des dpendances du
systme avec les menus droulants. Avant le JDK 5.0, vous deviez installer un couteur de souris et
ajouter le code suivant sur les mthodes dcouteur mousePressed et mouseReleased:
if (popup.isPopupTrigger(event))
popup.show(event.getComponent(), event.getX(), event.getY());

Certains systmes dclenchent des menus droulants lorsque la souris descend, dautres, lorsque la
souris remonte.
javax.swing.JPopupMenu 1.2

void show(Component c, int x, int y)

Affiche le menu contextuel.


Paramtres :

Le composant sur lequel le menu contextuel doit apparatre.

x, y

Les coordonnes (dans lespace de coordonnes de c)


du coin suprieur gauche du menu contextuel.

boolean isPopupTrigger(MouseEvent event) 1.3

Renvoie true si lvnement de la souris est le dclencheur du menu contextuel.


java.awt.event.MouseEvent 1.1

boolean isPopupTrigger()

Renvoie true si lvnement de la souris est le dclencheur du menu contextuel.


javax.swing.JComponent 1.2

void setComponentPopupMenu(JPopupMenu popup) 5.0

Livre Java .book Page 467 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

467

JPopup getComponentPopupMenu() 5.0

Dfinissent ou rcuprent le menu contextuel pour ce composant.

void setInheritsPopupMenu(boolean b) 5.0


boolean getInheritsPopupMenu() 5.0

Dfinissent ou rcuprent la proprit inheritsPopupMenu. Si la proprit est dfinie et que le


menu contextuel de ce composant soit null, elle utilise le menu contextuel de son parent.

Caractres mnmoniques et raccourcis clavier


Les utilisateurs expriments slectionnent souvent une option de menu par le biais dun caractre
mnmonique. Vous pouvez dfinir un caractre mnmonique en le spcifiant dans le constructeur de
loption de menu :
JMenuItem indexItem = new JMenuItem("Index", I);

Il apparat automatiquement sous forme dun soulignement de la lettre concerne dans le nom de
loption (voir Figure 9.25). Dans lexemple ci-dessus, il sagit de la lettre I de loption Index.
Quand le menu est droul, il suffit lutilisateur dappuyer sur la touche I pour slectionner
loption (lorsque la lettre utilise ne fait pas partie du libell, elle napparat pas dans le menu, mais
son activation permet nanmoins de slectionner loption. On peut bien entendu douter de lutilit de
caractres mnmoniques invisibles).
Figure 9.25
Caractres
mnmoniques.

Parfois, vous ne souhaiterez pas souligner la premire lettre de llment de menu correspondant au
caractre mnmonique. Si vous avez, par exemple, un "E" mnmonique pour le menu "Enregistrer
sous", vous pourriez souligner le deuxime "E". Depuis le JDK 1.4, vous pouvez spcifier le caractre qui sera soulign en appelant la mthode setDisplayedMnemonicIndex.
Si vous avez un objet Action, vous pouvez ajouter le caractre mnmonique comme valeur de la
touche Action.MNEMONIC_KEY, de la faon suivante :
indexAction.putValue(Index.MNEMONIC_KEY, new Integer(I));

Vous ne pouvez spcifier de caractre mnmonique que dans le constructeur dune option de menu,
et non dans le constructeur dun menu. Pour associer un caractre mnmonique un menu, vous
devez appeler la mthode setMnemonic:
JMenu helpMenu = new JMenu("Help");
helpMenu.setMnemonic(H);

Livre Java .book Page 468 Jeudi, 25. novembre 2004 3:04 15

468

Au cur de Java 2 - Notions fondamentales

Pour slectionner le nom dun menu principal dans la barre de menus, il faut maintenir enfonce la
touche Alt puis appuyer sur la touche mnmonique. Par exemple, si vous appuyez sur Alt-H, le menu
Help (Aide) sera slectionn.
Les touches mnmoniques permettent de slectionner une option ou un sous-menu partir dun
menu dj ouvert. Les combinaisons de touches ou acclrateurs sont des raccourcis clavier qui
permettent de slectionner des options sans avoir ouvrir le menu. Par exemple, de nombreux
programmes associent les combinaisons Ctrl+O et Ctrl+S aux options Ouvrir et Enregistrer du menu
Fichier. Pour lier une combinaison de touches une option, utilisez la mthode setAccelerator,
qui reoit un objet de type Keystroke. Lappel suivant associe la combinaison Ctrl+O loption
openItem (Ouvrir) :
openItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
InputEvent.CTRL_MASK));

Si lutilisateur excute cette combinaison de touches, loption de menu correspondante est slectionne et un vnement daction est dclench, comme dans le cas dune slection manuelle.
Vous ne pouvez associer de raccourcis clavier quaux options de menu, et non aux menus. Les
raccourcis clavier nouvrent pas le menu. Ils dclenchent directement lvnement daction associ
loption du menu.
Dun point de vue conceptuel, lajout dun raccourci clavier une option de menu est semblable la
technique dajout dun raccourci un composant Swing (nous avons tudi cette technique au
Chapitre 8). Cependant, lorsque le raccourci clavier est ajout une option de menu, la combinaison
de touches est automatiquement affiche en regard de loption (voir Figure 9.26).
Figure 9.26
Raccourcis clavier.

INFO
Sous Windows, la combinaison Alt+F4 ferme une fentre. Mais cet acclrateur na pas t programm sous Java. Il
sagit dun raccourci dfini par le systme dexploitation et qui dclenche toujours lvnement WindowClosing de
la fentre active, indpendamment du fait quil existe ou non une option de menu Fermer.

javax.swing.JMenuItem 1.2

JMenuItem(String label, int mnemonic)

Construit un lment de menu avec un intitul donn et un caractre mnmonique.


Paramtres :

label

Lintitul de loption de menu.

mnemonic

Le caractre mnmonique de loption ; il apparat soulign


dans lintitul.

Livre Java .book Page 469 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

469

void setAccelerator(KeyStroke k)

Dfinit la combinaison de touche k comme raccourci de loption de menu. La combinaison


saffiche en face de lintitul.
javax.swing.AbstractButton 1.2

void setMnemonic(int mnemonic)

Dfinit le caractre mnmonique du bouton. Il apparat soulign dans lintitul.


Paramtres :

mnemonic

Le caractre mnmonique du bouton.

void setDisplayedMnemonicIndex(int index) 1.4

Dfinit lindice du caractre souligner dans le texte du bouton. Utilisez cette mthode si la
premire occurrence du caractre mnmonique ne doit pas tre souligne.
Paramtres :

index

Lindice du caractre de texte du bouton souligner.

Activation et dsactivation des options de menu


Il arrive quune option de menu spcifique ne doive pouvoir tre slectionne que dans certains contextes. Par exemple, lorsquun document est ouvert en lecture seule, les options Enregistrer et Enregistrer
sous nont aucune utilit. Bien sr, on pourrait les supprimer du menu laide de la mthode
JMenu.remove, mais les utilisateurs seraient dsorients de voir son contenu changer ainsi. Par consquent, il vaut mieux dsactiver les options pouvant donner lieu des commandes inappropries. Une
option de menu dsactive apparat grise et ne peut pas tre slectionne (voir Figure 9.27).
Figure 9.27
Options
de menu dsactives.

Pour activer ou dsactiver une option de menu, utilisez la mthode setEnabled de la faon
suivante :
saveItem.setEnabled(false);

Il existe en fait deux faons de procder. Chaque fois quune situation change, vous pouvez appeler
la mthode setEnabled sur les options de menu ou les actions concernes. Par exemple, ds quun
document a t dfini en lecture seule, vous pouvez localiser les options Enregistrer et Enregistrer
sous et les dsactiver. Vous pouvez aussi dsactiver les lments juste avant que les options ne soient
affiches. Pour cela, vous devez enregistrer un couteur pour lvnement "menu slectionn".
Le package javax.swing.event dfinit une interface MenuListener comprenant trois mthodes :
void menuSelected(MenuEvent event)
void menuDeselected(MenuEvent event)
void menuCanceled(MenuEvent event)

Livre Java .book Page 470 Jeudi, 25. novembre 2004 3:04 15

470

Au cur de Java 2 - Notions fondamentales

La mthode menuSelected est appele avant que le menu ne soit affich. Par consquent, elle peut
tre utilise pour activer ou dsactiver une option. Le code suivant montre comment dsactiver les
actions Enregistrer et Enregistrer sous lorsque le mode Lecture seule est slectionn :
public void menuSelected(MenuEvent event)
{
saveAction.setEnabled(!readonlyItem.isSelected());
saveAsAction.setEnabled(!readonlyItem.isSelected());
}

ATTENTION
Dsactiver des lments de menu juste avant laffichage du menu est une ide brillante, mais cela ne fonctionne pas
pour ceux qui disposent aussi de touches acclrateur. Le menu ntant jamais ouvert la pression de la touche acclrateur, laction nest donc jamais dsactive ; elle est toujours dclenche par la touche acclrateur.

javax.swing.JMenuItem 1.2

void setEnabled(boolean b)

Active ou dsactive une option de menu.


javax.swing.event.MenuListener 1.2

void menuSelected(MenuEvent e)

Appele lorsquun menu a t slectionn, avant quil ne soit ouvert.

void menuDeselected(MenuEvent e)

Appele lorsquun menu a t dslectionn, aprs quil a t ferm.

void menuCanceled(MenuEvent e)

Appele lorsque le processus de slection du menu est abandonn, par exemple en cliquant en
dehors du menu.
LExemple 9.11 est un programme qui gnre un ensemble de menus. Il prsente toutes les fonctionnalits tudies dans cette section : menus imbriqus, options de menu dsactives, options avec
cases cocher et boutons radio, menus contextuels, caractres mnmoniques et raccourcis clavier.
Exemple 9.11 : MenuTest.java
import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;

public class MenuTest


{
public static void main(String[] args)
{
MenuFrame frame = new MenuFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}

Livre Java .book Page 471 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

/**
Un cadre avec une barre de menus.
*/
class MenuFrame extends JFrame
{
public MenuFrame()
{
setTitle("MenuTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
JMenu fileMenu = new JMenu("File");
JMenuItem newItem = fileMenu.add(new TestAction("New"));
// dmonstration des raccourcis clavier (acclrateurs)
JMenuItem openItem = fileMenu.add(new TestAction("Open"));
openItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_O, InputEvent.CTRL_MASK));
fileMenu.addSeparator();
saveAction = new TestAction("Save");
JMenuItem saveItem = fileMenu.add(saveAction);
saveItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_S, InputEvent.CTRL_MASK));
saveAsAction = new TestAction("Save As");
JMenuItem saveAsItem = fileMenu.add(saveAsAction);
fileMenu.addSeparator();
fileMenu.add(new
AbstractAction("Exit")
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
// dmonstration de menus avec cases cocher
// et boutons radio
readonlyItem = new JCheckBoxMenuItem("Read-only");
readonlyItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
boolean saveOk =!readonlyItem.isSelected();
saveAction.setEnabled(saveOk);
saveAsAction.setEnabled(saveOk);
}
});
ButtonGroup group = new ButtonGroup();
JRadioButtonMenuItem insertItem
= new JRadioButtonMenuItem("Insert");
insertItem.setSelected(true);

471

Livre Java .book Page 472 Jeudi, 25. novembre 2004 3:04 15

472

Au cur de Java 2 - Notions fondamentales

JRadioButtonMenuItem overtypeItem
= new JRadioButtonMenuItem("Overtype");
group.add(insertItem);
group.add(overtypeItem);
// dmonstration des icnes
Action cutAction = new TestAction("Cut");
cutAction.putValue(Action.SMALL_ICON,
new ImageIcon("cut.gif"));
Action copyAction = new TestAction("Copy");
copyAction.putValue(Action.SMALL_ICON,
new ImageIcon("copy.gif"));
Action pasteAction = new TestAction("Paste");
pasteAction.putValue(Action.SMALL_ICON,
new ImageIcon("paste.gif"));
JMenu editMenu = new JMenu("Edit");
editMenu.add(cutAction);
editMenu.add(copyAction);
editMenu.add(pasteAction);
// dmonstration de menus imbriqus
JMenu optionMenu = new JMenu("Options");
optionMenu.add(readonlyItem);
optionMenu.addSeparator();
optionMenu.add(insertItem);
optionMenu.add(overtypeItem);
editMenu.addSeparator();
editMenu.add(optionMenu);
// dmonstration de caractres mnmoniques
JMenu helpMenu = new JMenu("Help");
helpMenu.setMnemonic(H);
JMenuItem indexItem = new JMenuItem("Index");
indexItem.setMnemonic(I);
helpMenu.add(indexItem);
// ajout possible du caractre mnmonique une action
Action aboutAction = new TestAction("About");
aboutAction.putValue(Action.MNEMONIC_KEY,
new Integer(A));
helpMenu.add(aboutAction);
// ajouter tous les menus principaux la barre de menus
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
menuBar.add(fileMenu);
menuBar.add(editMenu);
menuBar.add(helpMenu);

Livre Java .book Page 473 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

473

// dmonstration de menu contextuel


popup = new JPopupMenu();
popup.add(cutAction);
popup.add(copyAction);
popup.add(pasteAction);
JPanel panel = new JPanel();
panel.setComponentPopupMenu(popup);
add(panel);
// la ligne suivante contourne le bogue 4966109
panel.addMouseListener(new MouseAdapter() {});
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
private
private
private
private

Action saveAction;
Action saveAsAction;
JCheckBoxMenuItem readonlyItem;
JPopupMenu popup;

}
/**
Un exemple daction qui affiche le nom de laction dans System.out
*/
class TestAction extends AbstractAction
{
public TestAction(String name) { super(name); }
public void actionPerformed(ActionEvent event)
{
System.out.println(getValue(Action.NAME)
+" selected.");
}
}

Barres doutils
Une barre doutils est une barre contenant des boutons qui permettent daccder rapidement aux
commandes le plus frquemment utilises dans un programme (voir Figure 9.28).
Figure 9.28
Une barre doutils.

Livre Java .book Page 474 Jeudi, 25. novembre 2004 3:04 15

474

Au cur de Java 2 - Notions fondamentales

La particularit des barres doutils est de pouvoir tre dplaces nimporte o. Vous pouvez faire
glisser la barre doutils vers lun des quatre bords du cadre (voir Figure 9.29). Lorsque vous relchez le bouton de la souris, la barre doutils est place au nouvel emplacement (voir
Figure 9.30).
Figure 9.29
Faire glisser la barre
doutils.

Figure 9.30
Faire glisser la barre
doutils vers une autre
bordure.

INFO
Le glissement dune barre doutils fonctionne si elle se trouve lintrieur dun conteneur avec une bordure, ou avec
tout autre gestionnaire de mise en forme grant les contraintes North, East, South et West.

La barre doutils peut mme tre compltement dtache du cadre. Elle est alors contenue dans son
propre cadre (voir Figure 9.31). Lorsque vous fermez le cadre contenant une barre doutils dtache,
celle-ci revient dans le cadre dorigine.
Figure 9.31
Dtacher la barre
doutils.

Livre Java .book Page 475 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

475

Les barres doutils sont assez simples programmer. Vous ajoutez les composants la barre
doutils :
JToolBar bar = new JToolBar();
bar.add(blueButton);

La classe JToolBar possde aussi une mthode pour ajouter un objet Action. Remplissez simplement la
barre doutils avec les objets Action, de la faon suivante :
bar.add(blueAction);

La petite icne de laction saffiche dans la barre doutils.


Vous pouvez sparer des groupes de boutons laide dun sparateur :
bar.addSeparator();

Par exemple, la barre doutils de la Figure 9.28 a un sparateur entre le troisime et le quatrime
bouton.
Vous ajoutez ensuite la barre doutils au cadre.
add(bar, BorderLayout.NORTH);

Vous pouvez aussi spcifier un titre pour la barre doutils, il apparatra lorsque la barre doutils sera
dtache :
bar = new JToolBar(titleString);

Par dfaut, les barres doutils sont positionnes horizontalement. Pour quune barre doutils soit
verticale, utilisez :
bar = new JToolBar(SwingConstants.VERTICAL)

ou
bar = new JToolBar(titleString, SwingConstants.VERTICAL)

Les boutons sont les composants les plus courants des barres doutils. Il ny a toutefois aucune
restriction en ce qui concerne les composants pouvant tre ajouts. Vous pouvez, par exemple, ajouter
une zone de liste droulante une barre doutils.
ASTUCE
Vous trouverez de jolis boutons de barre doutils ladresse http://developer.java.sun.com/developer/techDocs/hi/
repository.

Bulles daide
Un inconvnient des barres doutils est que les utilisateurs sont souvent dsorients par la signification des minuscules icnes qui y figurent. Des concepteurs dinterface utilisateur ont alors conu les
bulles daide. Une bulle daide est active lorsque le curseur stationne au-dessus dun bouton. Le
texte de la bulle daide apparat lintrieur dun rectangle color. Lorsque lutilisateur dplace la
souris, la bulle daide disparat (voir Figure 9.32).

Livre Java .book Page 476 Jeudi, 25. novembre 2004 3:04 15

476

Au cur de Java 2 - Notions fondamentales

Figure 9.32
Une bulle daide.

Dans Swing, vous pouvez ajouter des bulles daide nimporte quel objet JComponent simplement
en appelant la mthode setToolTipText:
exitButton.setToolTipText("Exit");

Vous pouvez aussi, si vous utilisez des objets Action, associer la bulle daide la touche laide de
SHORT_DESCRIPTION:
exitAction.putValue(Action.SHORT_DESCRIPTION, "Exit");

LExemple 9.12 est un programme montrant comment le mme objet Action peut tre ajout un
menu et une barre doutils. Notez que les noms dactions apparaissent comme noms des options
dans le menu et les courtes descriptions, comme bulles daide dans la barre doutils.
Exemple 9.12 : ToolBarTest.java
import
import
import
import

java.awt.*;
java.awt.event.*;
java.beans.*;
javax.swing.*;

public class ToolBarTest


{
public static void main(String[] args)
{
ToolBarFrame frame = new ToolBarFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec une barre doutils et un menu pour
choisir une nouvelle couleur.
*/
class ToolBarFrame extends JFrame
{
public ToolBarFrame()
{
setTitle("ToolBarTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter un panneau pour le changement de couleur
panel = new JPanel();

Livre Java .book Page 477 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

// configurer les actions


Action blueAction = new ColorAction("Blue",
new ImageIcon("blue-ball.gif"), Color.BLUE);
Action yellowAction = new ColorAction("Yellow",
new ImageIcon("yellow-ball.gif"), Color.YELLOW);
Action redAction = new ColorAction("Red",
new ImageIcon("red-ball.gif"), Color.RED);
Action exitAction = new
AbstractAction("Exit", new ImageIcon("exit.gif"))
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
};
exitAction.putValue(Action.SHORT_DESCRIPTION, "Exit");
// complter la barre doutils
JToolBar bar = new JToolBar();
bar.add(blueAction);
bar.add(yellowAction);
bar.add(redAction);
bar.addSeparator();
bar.add(exitAction);
add(bar, BorderLayout.NORTH);
// complter le menu
JMenu menu = new JMenu("Color");
menu.add(yellowAction);
menu.add(blueAction);
menu.add(redAction);
menu.add(exitAction);
JMenuBar menuBar = new JMenuBar();
menuBar.add(menu);
setJMenuBar(menuBar);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
private JPanel panel;
/**
Laction couleur dfinit larrire-plan du cadre
sur une couleur donne.
*/
class ColorAction extends AbstractAction
{
public ColorAction(String name, Icon icon, Color c)
{
putValue(Action.NAME, name);
putValue(Action.SMALL_ICON, icon);

477

Livre Java .book Page 478 Jeudi, 25. novembre 2004 3:04 15

478

Au cur de Java 2 - Notions fondamentales

putValue(Action.SHORT_DESCRIPTION,
" background"+name);
putValue("Color", c);
}
public void actionPerformed(ActionEvent event)
{
Color c = (Color) getValue("Color");
panel.setBackground(c);
}
}
}
javax.swing.JToolbar 1.2

JToolBar()
JToolBar(String titleString)
JToolBar(int orientation)
JToolBar(String titleString, int orientation)

Construisent une barre doutils avec le titre et lorientation donns. orientation est lune des
valeurs SwingConstants.HORIZONTAL (par dfaut) et SwingConstants.VERTICAL.

JButton add(Action a)

Construit un nouveau bouton dans la barre doutils, avec le nom, licne, une courte description
et le rappel daction (callback) de laction donne, et ajoute le bouton la fin de la barre doutils.

void addSeparator()

Ajoute un sparateur la fin de la barre doutils.


javax.swing.JComponent 1.2

void setToolTipText(String text)

Dfinit le texte qui doit tre affich comme bulle daide lorsque la souris stationne au-dessus du
composant.

Mise en forme sophistique


Nous avons russi jusqu prsent disposer les composants dinterface utilisateur de nos exemples
dapplication au moyen des gestionnaires BorderLayout, GridLayout et FlowLayout. Pour des
tches plus complexes, cette faon de procder sera insuffisante. Dans cette section, nous allons
examiner des gestionnaires de mise en forme avancs que la bibliothque Java standard propose pour
organiser les composants.
Les programmeurs Windows peuvent se demander pourquoi Java fait autant cas de ses gestionnaires
de mise en forme. Aprs tout, dans Windows, la gestion de mise en forme nest pas trs complique.
Tout dabord, vous utilisez un diteur de botes de dialogue pour organiser vos composants au
moyen de la fonction glisser-dposer, puis vous employez des outils ddition pour les aligner, les
espacer, les centrer, etc. Si vous travaillez sur un projet de grande ampleur, vous naurez probablement pas vous en proccuper, la mise en forme des composants sera prise en charge par un concepteur
dinterface.

Livre Java .book Page 479 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

479

Le problme de cette approche est que la mise en forme qui en rsulte doit tre actualise manuellement si la taille des lments dinterface change. Pourquoi leur taille changerait-elle ? Il y a deux
raisons cela. Tout dabord, un utilisateur peut choisir une police plus grande pour les libells de
bouton et autres textes de bote de dialogue. Si vous optez pour cet agrandissement en le testant
vous-mme dans Windows, vous vous apercevrez que de nombreuses applications le grent mdiocrement. Les boutons ne sagrandissent pas et les polices plus grandes sont confines dans le mme
espace. Le mme problme peut se produire lors de la traduction des chanes dune application dans
une autre langue. Si un bouton a t conu de telle faon que lespace corresponde strictement son
libell dorigine et que le mot traduit soit plus long (comme Save As qui devient Enregistrer sous en
franais), la version trangre apparatra tronque.
Pourquoi les boutons dans Windows ne sadaptent-ils pas aux libells ? Le concepteur de linterface
utilisateur na donn aucune instruction sur la direction dans laquelle ils devraient sagrandir. Aprs
leur placement et leur organisation, lditeur de botes de dialogue se souvient simplement de la
position en pixels et de la taille de chaque composant. Il ne se rappelle pas pourquoi les lments ont
t ainsi organiss.
Les gestionnaires de mise en forme proposent une bien meilleure approche de lorganisation
dune interface. La mise en forme dtient alors des instructions sur les relations entre les diffrents composants, ce qui tait particulirement important avec la bibliothque AWT dorigine,
qui utilisait des lments dinterface natifs. La taille dun bouton ou dune zone de liste dans
Motif, Windows et Macintosh pouvait varier considrablement, et une application ou un applet
ignorait a priori sur quelle plate-forme il afficherait son interface utilisateur. Dans une certaine
limite, ce degr de variabilit a t limin avec Swing. Si votre application implmente un
certain style dinterface dynamique, comme Metal, elle aura la mme apparence sur toutes les
plates-formes. Toutefois, si vous laissez les utilisateurs de votre application choisir leur look and
feel favori, vous devrez nouveau faire appel la souplesse des gestionnaires pour disposer les
composants.
Bien sr, pour raliser des interfaces complexes, vous devrez disposer dun niveau de contrle
sur la mise en forme, suprieur ce que les gestionnaires BorderLayout, GridLayout et
FlowLayout vous accordent. Dans cette section, nous traiterons de tous les gestionnaires proposs par la bibliothque Java standard. En combinant lusage dun gestionnaire sophistiqu avec
plusieurs panneaux appropris, vous obtiendrez un contrle total sur lapparence de votre application.
ASTUCE
Si aucun des schmas de mise en forme ne convient vos besoins, divisez la surface de votre fentre en diffrents
panneaux et disposez-les sparment. Utilisez ensuite un autre gestionnaire pour organiser les panneaux.

Revoyons certains principes de base. Comme vous le savez, dans AWT les composants sont disposs
dans des conteneurs. Les boutons, les champs de texte et autres lments dinterface sont des
composants qui peuvent tre placs lintrieur de conteneurs. Par consquent, leurs classes tendent la classe Component. Des conteneurs tels que des panneaux peuvent aussi tre placs dans

Livre Java .book Page 480 Jeudi, 25. novembre 2004 3:04 15

480

Au cur de Java 2 - Notions fondamentales

dautres conteneurs. Par consquent, la classe Container drive de Component. La Figure 9.33
illustre la hirarchie dhritage pour Component.

Object

Component

Container

Window

JComponent

Frame

Dialog

JFrame

JDialog

JPanel

JText
Component

JTextField

JLabel

JScrollPane

JTextArea

JComboBox

Abstract
Button

JMenuBar

JButton

JToggle
Button

JMenuItem

Figure 9.33
Hirarchie dhritage pour la classe Component.

INFO
Certains objets appartiennent aux classes qui tendent Component mme sils ne sont pas des composants dinterface utilisateur et ne peuvent pas tre insrs dans des conteneurs. Les fentres de haut niveau comme JFrame et
JApplet ne peuvent pas tre contenues lintrieur dune autre fentre ou dun autre panneau.

Comme vous lavez constat, pour organiser les composants dans un conteneur, vous devez tout
dabord spcifier un gestionnaire de mise en forme. Par exemple, linstruction
panel.setLayout(new GridLayout(4, 4));

utilisera la classe GridLayout pour placer les panneaux. Aprs avoir dfini le gestionnaire, ajoutez
des composants au conteneur. La mthode add de ce dernier passe le composant et toutes les directives de
placement au gestionnaire.
Avec le gestionnaire BorderLayout, vous passez une chane pour spcifier le placement du
composant :
panel.add(new JTextField(), BorderLayout.SOUTH);

Livre Java .book Page 481 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

481

Avec GridLayout, vous devez ajouter les composants en squence :


panel.add(new JCheckBox("italic"));
panel.add(new JCheckBox("bold"));

Le gestionnaire GridLayout est utile pour organiser les composants dans une grille, un peu la
manire des lignes et des colonnes dun tableur. Toutefois, toutes les lignes et les colonnes de la grille
ont une taille identique, ce qui nest pas toujours pratique.
Pour contourner les limitations de ce gestionnaire, la bibliothque AWT en fournit un autre, GridBagLayout. Son organisation suit aussi un modle en lignes et en colonnes, mais les dimensions de ces
lignes et colonnes sont variables et les composants peuvent en occuper plusieurs. Ce gestionnaire est
trs souple, mais aussi trs complexe. En fait, dans la plupart des situations courantes, il nest pas si
difficile employer, et nous vous indiquerons une stratgie pour vous en faciliter lusage.
Lors dune tentative (infructueuse) de mise au point dun gestionnaire qui affranchirait les programmeurs de la tyrannie du GridBagLayout, les concepteurs de Swing ont produit le gestionnaire
BoxLayout. Il organise simplement horizontalement ou verticalement une squence de composants.
Dans le premier cas, la mise en forme de la case ressemble au FlowLayout, except que les composants ne passent pas la ligne lorsquune ligne est pleine. En plaant un certain nombre de BoxLayout
horizontaux dans un BoxLayout vertical (ou linverse), vous pouvez relativement ordonner un ensemble de composants dans une zone en deux dimensions. Comme les BoxLayout sont placs indpendamment les uns des autres, vous ne pouvez pas les utiliser pour organiser des composants voisins la
fois horizontalement et verticalement.
Le JDK 1.4 a connu une autre tentative pour concevoir le remplaant du GridBagLayout, la mise en
forme spring. Nous verrons la mise en forme spring plus loin dans ce chapitre. Libre vous de dcider
si elle remplit son rle.
Swing contient aussi un gestionnaire OverlayLayout qui vous permet de placer les lments les uns
au-dessus des autres. Il nest pas dune grande utilit, cest pourquoi nous nen parlerons pas ici.
Enfin, un gestionnaire CardLayout tait utilis dans lensemble AWT dorigine pour produire des
botes de dialogue avec onglets. Puisque Swing possde un conteneur avec onglets plus performant
(appel JTabbedPane), nous ne le dcrirons pas ici.
Nous conclurons en vous indiquant comment ignorer compltement les gestionnaires et disposer les
composants manuellement, et en vous expliquant comment crire votre propre gestionnaire.

Gestionnaire BoxLayout
Ce gestionnaire vous permet de placer une seule ligne ou colonne de composants avec plus de
souplesse que GridLayout. Il y a mme un conteneur, la classe Box, dont le gestionnaire par dfaut
est BoxLayout alors que celui par dfaut de la classe JPanel est FlowLayout. Bien sr, vous
pouvez aussi affecter BoxLayout pour quil soit le gestionnaire dun JPanel, mais il est plus simple
de partir dun conteneur Box. La classe Box contient aussi un certain nombre de mthodes statiques
utiles la gestion de BoxLayout.
Pour crer un nouveau conteneur BoxLayout, vous pouvez simplement appeler :
Box b = Box.createHorizontalBox();

ou :
Box b = Box.createVerticalBox();

Livre Java .book Page 482 Jeudi, 25. novembre 2004 3:04 15

482

Au cur de Java 2 - Notions fondamentales

Ensuite, vous ajoutez des composants de la manire habituelle :


b.add(okButton);
b.add(cancelButton);

Dans un conteneur Box horizontal, les composants sont placs de la gauche vers la droite. Dans un
Box vertical, ils sont disposs du haut vers le bas. Etudions la mise en forme horizontale de plus prs.
Chaque composant a trois valeurs :
m

la taille prfre, la largeur et la hauteur prfres auxquelles afficher le composant ;

la taille maximale, la largeur et la hauteur maximales auxquelles afficher le composant ;

la taille minimale, la largeur et la hauteur minimales auxquelles afficher le composant.

Voici en dtail ce que le gestionnaire BoxLayout ralise :


1. Il calcule la taille maximale du composant le plus grand en hauteur.
2. Il tente dagrandir verticalement tous les composants jusqu cette hauteur.
3. Si un composant natteint pas cette taille lorsque cela est demand, son alignement y est requis
en appelant sa mthode getAlignmentY. Celle-ci renvoie un nombre virgule flottante entre 0
(alignement en haut) et 1 (alignement en bas). La valeur par dfaut dans la classe Component est
0,5 (centr). La valeur est utilise pour aligner le composant verticalement.
4. La largeur prfre de chaque composant est extraite. Toutes les largeurs prfres sont additionnes.
5. Si la largeur totale prfre est infrieure la largeur de Box, les composants sont tirs, en les
laissant sagrandir jusqu leur largeur maximale. Ils sont ensuite placs de la gauche vers la
droite, sans intervalles supplmentaires de sparation. Si la largeur totale prfre est suprieure
celle de Box, les composants sont rduits, ventuellement jusqu leur largeur minimale, mais
pas davantage. Sils ne tiennent pas dans Box avec leur largeur minimale, certains ne seront pas
affichs.
Pour les mises en forme verticales, le processus est analogue.
ASTUCE
Il est dommage que BoxLayout tente dagrandir les composants au-del de la taille prfre. En particulier, les
champs de texte ont une largeur et une hauteur maximales dfinies Integer.MAX_VALUE; cest--dire quils
tentent de sagrandir autant que ncessaire. Si vous placez un champ de texte dans un objet BoxLayout, il atteindra
des proportions monstrueuses. Le remde consiste affecter la taille prfre la taille maximale :
textField.setMaximumSize(textField.getPreferredSize());

Rserves
Par dfaut, il ny a pas despace entre les composants avec un gestionnaire BoxLayout. A la diffrence de FlowLayout, il ny a pas de notion dintervalle entre les composants. Pour espacer des
lments, vous ajoutez des rserves invisibles (fillers). Il en existe trois types :
m

Strut ;

RigidArea ;

Glue.

Livre Java .book Page 483 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

483

Un Strut ajoute un espace entre les composants. Par exemple, voici comment vous pouvez insrer
un espace de dix pixels entre deux boutons dans un conteneur Box horizontal :
b.add(label);
b.add(Box.createHorizontalStrut(10));
b.add(textField);

Vous ajoutez un Strut horizontal dans un conteneur Box horizontal, ou un Strut vertical dans un
conteneur Box vertical pour insrer un espace entre les composants. Vous pouvez aussi ajouter un
Strut vertical dans un conteneur Box horizontal, mais cela ninflue pas sur la mise en forme horizontale. Cela dfinit la place la hauteur minimale de Box.
RigidArea est analogue une paire de Strut. Il spare des composants adjacents, mais ajoute aussi
une hauteur ou une largeur minimale dans lautre direction. Par exemple,
b.add(Box.createRigidArea(new Dimension(5, 20));

ajoute un espace invisible avec une largeur minimale, prfre et maximale de 5 pixels, une hauteur
de 20 pixels et un alignement centr. Sil est ajout dans un conteneur Box horizontal, il agit comme
un Strut dune largeur de 5 et force aussi une hauteur minimale de 20 pixels pour Box.
En ajoutant des struts, vous sparez des composants adjacents par un espace fixe. Lajout dun Glue
spare les composants autant que possible. Un Glue (invisible) sagrandit au maximum de lespace
disponible, en loignant les composants les uns des autres.
Par exemple, voici comment vous espacez au maximum deux boutons dans un conteneur Box:
b.add(button1);
b.add(Box.createGlue());
b.add(button2);

Si Box ne contient pas dautres composants, button1 est dplac compltement vers la gauche et
button2 est dplac vers lextrme droite.
Le programme de lExemple 9.13 dispose un ensemble de libells, de champs de texte et de boutons,
laide dun jeu dobjets BoxLayout horizontaux et verticaux. Chaque ligne est place dans un
conteneur Box horizontal. Des Strut sparent les libells des champs de texte. Les Glue loignent
les deux boutons lun de lautre. Les trois Box horizontaux sont placs dans un Box vertical, avec un
Glue repoussant le conteneur Box des boutons vers le bas (voir Figure 9.34).
Figure 9.34
Mises en forme
dun conteneur Box.

strut

glue

glue

Livre Java .book Page 484 Jeudi, 25. novembre 2004 3:04 15

484

Au cur de Java 2 - Notions fondamentales

Exemple 9.13 : BoxLayoutTest.java


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BoxLayoutTest
{
public static void main(String[] args)
{
BoxLayoutFrame frame = new BoxLayoutFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre qui utilise des objets BoxLayout pour organiser
diffrents composants.
*/
class BoxLayoutFrame extends JFrame
{
public BoxLayoutFrame()
{
setTitle("BoxLayoutTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// construire lobjet Box horizontal suprieur
JLabel label1 = new JLabel("Name:");
JTextField textField1 = new JTextField(10);
textField1.setMaximumSize(textField1.getPreferredSize());
Box hbox1 = Box.createHorizontalBox();
hbox1.add(label1);
// sparer laide dun strut de 10pixels
hbox1.add(Box.createHorizontalStrut(10));
hbox1.add(textField1);
// construire lobjet Box horizontal central
JLabel label2 = new JLabel("Password:");
JTextField textField2 = new JTextField(10);
textField2.setMaximumSize(textField2.getPreferredSize());

Box hbox2 = Box.createHorizontalBox();


hbox2.add(label2);
// sparer laide dun strut de 10pixels
hbox2.add(Box.createHorizontalStrut(10));
hbox2.add(textField2);
// construire lobjet Box horizontal infrieur

Livre Java .book Page 485 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

485

JButton button1 = new JButton("OK");


JButton button2 = new JButton("Cancel");
Box hbox3 = Box.createHorizontalBox();
hbox3.add(button1);
// utiliser "glue" pour loigner les deux boutons
hbox3.add(Box.createGlue());
hbox3.add(button2);
// ajouter les trois objets Box horizontaux
// lintrieur dun Box vertical
Box vbox = Box.createVerticalBox();
vbox.add(hbox1);
vbox.add(hbox2);
vbox.add(Box.createGlue());
vbox.add(hbox3);
add(vbox, BorderLayout.CENTER);
}
public static final int DEFAULT_WIDTH = 200;
public static final int DEFAULT_HEIGHT = 200;
}
javax.swing.Box 1.2

static Box createHorizontalBox()


static Box createVerticalBox()

Crent un conteneur qui organise son contenu horizontalement ou verticalement.

static Component createHorizontalGlue()


static Component createVerticalGlue()
static Component createGlue()

Crent un composant invisible qui peut stendre linfini horizontalement, verticalement ou


dans les deux directions.

static Component createHorizontalStrut(int width)


static Component createVerticalStrut(int height)
static Component createRigidArea(Dimension d)

Crent un composant invisible avec une largeur ou une hauteur fixe, ou les deux dimensions
fixes.
java.awt.Component 1.0

float getAlignmentX() 1.1


float getAlignmentY() 1.1

Renvoient lalignement le long de laxe x ou y, avec une valeur entre 0 et 1. La valeur 0 indique
un alignement en haut ou gauche, 0,5 un alignement centr et 1 un alignement en bas ou
droite.

Livre Java .book Page 486 Jeudi, 25. novembre 2004 3:04 15

486

Au cur de Java 2 - Notions fondamentales

Gestionnaire GridBagLayout
Vous pouvez considrer le gestionnaire GridBagLayout comme un GridLayout sans ses limitations. Dans un GridBagLayout, les lignes et les colonnes peuvent avoir des tailles variables.
Vous pouvez joindre des cellules adjacentes afin de prvoir la place pour des composants plus
grands. De nombreux programmes de traitement de texte ainsi que des diteurs HTML possdent la mme fonctionnalit lorsquils grent des tableaux. Vous commencez avec une grille et
fusionnez des cellules adjacentes si besoin est. Il nest pas ncessaire que les composants
remplissent la zone entire dune cellule, et vous pouvez spcifier leur alignement lintrieur
des cellules.
Sachez que lemploi des gestionnaires GridBagLayout peut se rvler incroyablement
complexe. Lavantage est quils offrent la plus grande souplesse et fonctionneront dans la plupart
des situations. Gardez lesprit que lobjectif des gestionnaires de mise en forme est dassurer la
disposition des composants avec diffrentes polices sous divers systmes dexploitation. Vous
aurez donc fournir quelques efforts supplmentaires par rapport la conception dune interface
pour un seul environnement.
INFO
Selon la documentation JDK de la classe BoxLayout: "limbrication de plusieurs panneaux avec diffrentes combinaisons de [sic] horizontal et vertical (!) donne un effet analogue GridBagLayout, sans la complexit". Pourtant,
comme vous le voyez la Figure 9.34, leffet que vous pouvez obtenir partir de plusieurs BoxLayout nest pas trs
utile dans la pratique. Aucune combinaison complique de Box, Strut et Glue nassurera un alignement des
composants. Si vous voulez organiser des composants pour quils soient aligns horizontalement et verticalement,
envisagez dutiliser la classe GridBagLayout.

Examinez la bote de dialogue de slection de polices de la Figure 9.35. Elle se compose des
lments suivants :
m

deux zones de liste droulantes pour spcifier la police et sa taille ;

des libells pour ces deux listes combines ;

deux cases cocher pour slectionner les attributs Gras et Italique ;

un champ de texte pour lexemple de chane.

Figure 9.35
La bote de dialogue
de slection de polices.

Livre Java .book Page 487 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

487

Maintenant, divisez la bote en une grille de cellules, comme le montre la Figure 9.36 (les lignes et
les colonnes nont pas besoin dtre de taille gale). Comme vous pouvez le constater, chaque case
cocher stend sur 2 colonnes et la zone de texte sur 4 lignes.
Figure 9.36
Une grille de bote
de dialogue utilise pour
la conception.

Pour fournir la mise en forme au gestionnaire GridBagLayout, vous devez suivre la procdure ciaprs :
1. Crez un objet de type GridBagLayout. Ne lui indiquez pas le nombre de lignes et de colonnes
que la grille sous-jacente doit possder. Le gestionnaire essayera de le deviner laide des informations que vous fournirez plus tard.
2. Dfinissez cet objet comme tant le gestionnaire de mise en forme pour le composant.
3. Pour chaque composant, crez un objet de type GridBagConstraints. Dfinissez les valeurs de
champ de lobjet GridBagConstraints pour spcifier de quelle faon les composants sont
disposs lintrieur du GridBag.
4. Ajoutez enfin le composant avec les contraintes au moyen dun appel :
add(composant, contraintes);

Voici un exemple du code requis. Nous tudierons plus en dtail les contraintes par la suite. Ne vous
inquitez pas si vous ignorez leffet de certaines contraintes.
GridBagLayout layout = new GridBagLayout();
panel.setLayout(layout);
GridBagConstraints constraints = new GridBagConstraints();
constraints.weightx = 100;
constraints.weighty = 100;
constraints.gridx = 0;
constraints.gridy = 2;
constraints.gridwidth = 2;
constraints.gridheight = 1;
panel.add(style, bold);

Lastuce est de savoir comment dfinir ltat de lobjet GridBagConstraints. Nous passerons en
revue les contraintes les plus importantes pour lemploi de cet objet dans les sections qui suivent.

Livre Java .book Page 488 Jeudi, 25. novembre 2004 3:04 15

488

Au cur de Java 2 - Notions fondamentales

Les paramtres gridx, gridy, gridwidth et gridheight


Ces contraintes dfinissent lemplacement du composant dans la grille. Les valeurs gridx et gridy
spcifient les positions de colonne et de ligne du coin suprieur gauche du composant ajouter. Les
valeurs gridwidth et gridheight dterminent le nombre de colonnes et de lignes que le composant
occupe.
Les coordonnes de la grille commencent 0. En particulier, gridx = 0 et gridy = 0 indiquent le
coin suprieur gauche.
Par exemple, la zone de texte dans notre exemple a les valeurs gridx = 2, gridy = 0, car elle
dmarre la colonne 2 (cest--dire la troisime colonne) de la ligne 0. Elle a les valeurs gridwidth
= 1 et gridheight = 4, car elle stend sur une colonne et quatre lignes.

Champs de poids
Vous devez toujours dfinir les champs de poids (weightx et weighty) pour chaque zone dans un
GridBagLayout. Si vous dfinissez le poids 0, la zone ne sagrandit ou ne diminue jamais audel de sa taille initiale dans cette direction. Dans la mise en forme illustre la Figure 9.35, nous
avons dfini le champ weightx des tiquettes 0. Cela leur permet de conserver une largeur
constante lorsque vous redimensionnez la fentre. En revanche, si vous dfinissez les poids pour
toutes les zones 0, le conteneur se ramassera au centre de sa zone alloue au lieu de stendre
pour loccuper pleinement.
Du point de vue conceptuel, le problme avec les paramtres de poids est que les poids sont des
proprits des lignes et des colonnes, et non des cellules individuelles. Mais vous devez les spcifier
en termes de cellules, puisque GridBagLayout nexpose pas les lignes et les colonnes. Les poids de
lignes et de colonnes sont calculs comme les maxima des poids de cellule dans chaque ligne ou
colonne. Par consquent, si vous voulez quune ligne ou quune colonne reste une taille fixe, vous
devez dfinir les poids de tous ses composants zro.
Notez que les poids ne donnent pas rellement les tailles relatives des colonnes. Ils indiquent quelle
proportion de lespace non appliqu devrait tre allou chaque zone, si le conteneur dpasse sa
taille prfre. Ce nest pas particulirement intuitif. Nous vous recommandons de mettre tous les
poids 100. Excutez ensuite le programme et observez lapparence de la mise en forme. Redimensionnez la bote de dialogue pour vrifier la faon dont les lignes et les colonnes sadaptent. Si vous
jugez quune ligne ou colonne particulire ne devrait pas sagrandir, dfinissez les poids de tous les
composants qui sy trouvent zro. Vous pouvez remanier les autres valeurs de poids, mais cette
opration est sans intrt la plupart du temps.

Les paramtres fill et anchor


Si vous ne voulez pas quun composant stire et remplisse toute la zone, vous devez dfinir la contrainte
fill. Vous disposez de quatre options pour ce paramtre : les valeurs valides sont GridBagConstraints.NONE (aucun), GridBagConstraints.HORIZONTAL, GridBagConstraints.VERTICAL et
GridBagConstraints.BOTH (les deux).
Si le composant ne remplit pas la zone entire, vous pouvez spcifier sa situation dans la zone en dfinissant le champ anchor. Les valeurs valides sont : GridBagConstraints.CENTER (valeur par dfaut),
GridBagConstraints.NORTH, GridBagConstraints.NORTHEAST, GridBagConstraints.EAST, etc.

Livre Java .book Page 489 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

489

Remplissage
Vous pouvez entourer un composant dun espace vierge supplmentaire en dfinissant le champ
insets de GridBagConstraints. Dfinissez les valeurs left, top, right et bottom de lobjet
Insets avec les valeurs despace que vous voulez avoir autour du composant. Cest le remplissage
externe.
Les valeurs ipadx et ipady dfinissent le remplissage interne. Ces valeurs sont ajoutes aux largeur
et hauteur minimales du composant. Cela garantit que le composant ne diminuera pas jusqu sa
taille minimale.

Autre mthode de dfinition des paramtres gridx, gridy, gridwidth et gridheight


La documentation AWT recommande de remplacer la dfinition des paramtres gridx et gridy en
valeurs absolues par la constante GridBagConstraints.RELATIVE. Ajoutez ensuite les composants
GridBagLayout en suivant une squence standard, de la gauche vers la droite pour la premire
ligne, puis en passant la ligne suivante, etc.
Spcifiez toujours le nombre de colonnes et de lignes occupes en fournissant les champs gridwidth et gridheight. Sauf si le composant stend jusqu la dernire ligne ou colonne, vous
ntes pas cens spcifier le nombre rel, mais la constante GridBagConstraints.REMAINDER.
Cela signale au gestionnaire de mise en forme que le composant est le dernier sur sa ligne.
Cette stratgie ne semble pas fonctionner. Mais il parat un peu insens de vouloir dissimuler les
informations relles de placement au gestionnaire et esprer quil les redcouvre.
Tout cela parat plutt complexe et fastidieux. En suivant les tapes ci-aprs, les difficults demploi
des GridBagLayout sont quasi inexistantes.

Conseils pour crer un GridBagLayout


Etape 1.

Esquissez la mise en forme du composant sur une feuille.

Etape 2.

Dterminez une grille afin que les petits composants soient chacun contenus dans une
cellule et que les grands puissent en occuper plusieurs.

Etape 3.

Libellez les lignes et les colonnes de votre grille avec des chiffres : 0, 1, 2, 3 Vous pouvez
maintenant relever les valeurs pour gridx, gridy, gridwidth et gridheight.

Etape 4.

Pour chaque composant, demandez-vous sil doit remplir ses cellules horizontalement ou
verticalement, ou comment il doit tre align. Cela vous donne les paramtres fill et
anchor.

Etape 5.

Dfinissez tous les poids 100. Si vous souhaitez cependant quune ligne ou colonne
particulire reste toujours sa taille par dfaut, dfinissez les valeurs de weightx ou
weighty 0 pour tous les composants qui appartiennent cette ligne ou colonne.

Etape 6.

Ecrivez le code. Vrifiez attentivement vos paramtres pour GridBagConstraints. Une


contrainte errone peut gcher toute votre mise en forme.

Etape 7.

Compilez, excutez et apprciez le tout.

Livre Java .book Page 490 Jeudi, 25. novembre 2004 3:04 15

490

Au cur de Java 2 - Notions fondamentales

Une classe daide pour matriser GridBagConstraints


Laspect le plus assommant du GridBagLayout est dcrire le code qui dfinit les contraintes. La
plupart des programmeurs crivent des fonctions daide ou une petite classe daide dans ce but. Nous
prsentons cette classe aprs le code complet pour lexemple de la bote de dialogue des polices.
Cette classe prsente les aspects suivants :
m

Son nom est court, GBC au lieu de GridBagConstraints.

Elle tend GridBagConstraints, vous pouvez donc utiliser des noms plus courts comme
GBC.EAST pour les constantes.

Utilisez un objet GBC lorsque vous ajoutez un composant, comme


add(component, new GBC(1, 2));

Il existe deux constructeurs pour dfinir les paramtres les plus communs : gridx et gridy ou
gridx, gridy, gridwidth et gridheight:
add(component, new GBC(1, 2, 1, 4));

Il existe des moyens de dfinition commodes pour les champs qui viennent en paires x/y:
add(component, new GBC(1, 2).setWeight(100, 100));

Les mthodes de dfinition renvoient this, vous pouvez donc les enchaner :
add(component, new GBC(1, 2).setAnchor(GBC.EAST).setWeight(100, 100));

Les mthodes setInsets construisent lobjet Insets pour vous. Pour obtenir des insets dun
pixel, appelez simplement :
add(component, new GBC(1, 2).setAnchor(GBC.EAST).setInsets(1));

LExemple 9.14 donne le code complet pour la bote de dialogue de polices. Voici le code qui ajoute
les composants au GridBag:
add(faceLabel, new GBC(0, 0).setAnchor(GBC.EAST));
add(face, new GBC(1, 0).setFill(GBC.HORIZONTAL).
setWeight(100, 0).setInsets(1));
add(sizeLabel, new GBC(0, 1).setAnchor(GBC.EAST));
add(size, new GBC(1, 1).setFill(GBC.HORIZONTAL).
setWeight(100, 0).setInsets(1));
add(bold, new GBC(0, 2, 2, 1).setAnchor(GBC.CENTER).setWeight(100, 100));
add(italic, new GBC(0, 3, 2, 1).setAnchor(GBC.CENTER).
setWeight(100, 100));
add(sample, new GBC(2, 0, 1, 4).setFill(GBC.BOTH).setWeight(100, 100));

Lorsque vous aurez compris GridBagConstraints, vous verrez que ce type de code est assez
simple lire et dboguer.
INFO
Le didacticiel Sun, ladresse http://java.sun.com/docs/books/tutorial/uiswing/layout/gridbag.html, vous suggre
de rutiliser le mme objet GridBagConstraints pour tous les composants. Nous considrons que le code de rsultat est difficile lire et sujet erreur. Etudiez par exemple la dmo de ladresse http://java.sun.com/docs/ books/
tutorial/uiswing/events/containerlistener.html. Lobjectif tait-il vraiment que les boutons stirent lhorizontale
ou le programmeur a-t-il juste oubli de dsactiver le paramtre BOTH pour la contrainte fill?

Livre Java .book Page 491 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

Exemple 9.14 : FontDialog.java


import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;

public class FontDialog


{
public static void main(String[] args)
{
FontDialogFrame frame = new FontDialogFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre qui utilise GridBagLayout pour organiser
les composants de slection de police.
*/
class FontDialogFrame extends JFrame
{
public FontDialogFrame()
{
setTitle("FontDialog");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
GridBagLayout layout = new GridBagLayout();
setLayout(layout);
ActionListener listener = new FontAction();
// construire les composants
JLabel faceLabel = new JLabel("Face: ");
face = new JComboBox(new String[]
{
"Serif", "SansSerif", "Monospaced",
"Dialog", "DialogInput"
});
face.addActionListener(listener);
JLabel sizeLabel = new JLabel("Size: ");
size = new JComboBox(new String[]
{
"8", "10", "12", "15", "18", "24", "36", "48"
});
size.addActionListener(listener);
bold = new JCheckBox("Bold");
bold.addActionListener(listener);

491

Livre Java .book Page 492 Jeudi, 25. novembre 2004 3:04 15

492

Au cur de Java 2 - Notions fondamentales

italic = new JCheckBox("Italic");


italic.addActionListener(listener);
sample = new JTextArea();
sample.setText("The quick brown fox jumps over the lazy dog");
sample.setEditable(false);
sample.setLineWrap(true);
sample.setBorder(BorderFactory.createEtchedBorder());
// ajouter les composants la grille, laide de la classe GBC
add(faceLabel, new GBC(0, 0).setAnchor(GBC.EAST));
add(face, new GBC(1, 0).setFill(GBC.HORIZONTAL).
setWeight(100, 0).setInsets(1));
add(sizeLabel, new GBC(0, 1).setAnchor(GBC.EAST));
add(size, new GBC(1, 1).setFill(GBC.HORIZONTAL).
setWeight(100, 0).setInsets(1));
add(bold, new GBC(0, 2, 2, 1).setAnchor(GBC.CENTER).
setWeight(100, 100));
add(italic, new GBC(0, 3, 2, 1).setAnchor(GBC.CENTER).
setWeight(100, 100));
add(sample, new GBC(2, 0, 1, 4).setFill(GBC.BOTH).
setWeight(100, 100));
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
private
private
private
private
private

JComboBox
JComboBox
JCheckBox
JCheckBox
JTextArea

face;
size;
bold;
italic;
sample;

/**
Un couteur daction qui change la police de
lexemple de texte.
*/
private class FontAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String fontFace = (String) face.getSelectedItem();
int fontStyle = (bold.isSelected()? Font.BOLD: 0)
+(italic.isSelected()? Font.ITALIC: 0);
int fontSize = Integer.parseInt(
(String)size.getSelectedItem());
Font font = new Font(fontFace, fontStyle, fontSize);
sample.setFont(font);
sample.repaint();
}
}
}

LExemple 9.15 prsente le code de la classe daide GBC.

Livre Java .book Page 493 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

Exemple 9.15 : GBC.java


/*
GBC Une classe pour matriser le GridBagLayout
Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com)
Ce programme est un logiciel libre. Vous pouvez le distribuer et/ou
le modifier aux termes des conditions du "GNU General Public License"
publi par Free Software Foundation (version2 de la Licence ou
( votre choix) toute version ultrieure).
Ce programme est distribu dans lespoir quil sera utile,
mais SANS AUCUNE GARANTIE, ni mme la garantie implicite de
QUALITE MARCHANDE ou DADAPTATION A UN OBJECTIF PARTICULIER.
Voir le GNU General Public License pour en savoir plus.
Vous devriez avoir reu une copie du GNU General Public License
avec ce programme; dans le cas contraire, crivez Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import java.awt.*;
/**
Cette classe simplifie lutilisation de la classe GridBagConstraints.
*/
public class GBC extends GridBagConstraints
{
/**
Construit un GBC avec une position gridx et gridy donne
et toutes les autres valeurs de GridBagConstraints dfinies
sur le paramtre par dfaut.
@param gridx La position gridx
@param gridy La position gridy
*/
public GBC(int gridx, int gridy)
{
this.gridx = gridx;
this.gridy = gridy;
}
/**
Construit un GBC avec gridx, gridy, gridwidth, gridheight
et toutes les autres valeurs de GridBagConstraints dfinies sur la
valeur par dfaut.
@param gridx La position gridx
@param gridy La position gridy
@param gridwidth Ltirement de cellule dans la direction x
@param gridheight Ltirement de cellule dans la direction y
*/
public GBC(int gridx, int gridy, int gridwidth, int gridheight)
{
this.gridx = gridx;
this.gridy = gridy;

493

Livre Java .book Page 494 Jeudi, 25. novembre 2004 3:04 15

494

Au cur de Java 2 - Notions fondamentales

this.gridwidth = gridwidth;
this.gridheight = gridheight;
}
/**
Dfinit lancrage.
@param anchor La valeur de lancrage
@return this Objet pour une future modification
*/
public GBC setAnchor(int anchor)
{
this.anchor = anchor;
return this;
}
/**
Dfinit la direction de fill.
@param fill La direction de fill
@return this Objet pour une future modification
*/
public GBC setFill(int fill)
{
this.fill = fill;
return this;
}
/**
Dfinit les poids de cellule.
@param weightx Le poids de cellule dans la direction x
@param weighty Le poids de cellule dans la direction y
@return this Objet pour une future modification
*/
public GBC setWeight(double weightx, double weighty)
{
this.weightx = weightx;
this.weighty = weighty;
return this;
}
/**
Dfinit les insets de cette cellule.
@param distance Lespacement utiliser dans toutes les directions
@return this Objet pour une future modification
*/
public GBC setInsets(int distance)
{
this.insets = new Insets(distance, distance, distance, distance);
return this;
}
/**
Dfinit les insets de cette cellule.
@param top Lespacement utiliser en haut

Livre Java .book Page 495 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

495

@param left Lespacement utiliser gauche


@param bottom Lespacement utiliser en bas
@param right Lespacement utiliser droite
@return this Objet pour une future modification
*/
public GBC setInsets(int top, int left, int bottom, int right)
{
this.insets = new Insets(top, left, bottom, right);
return this;
}
/**
Dfinit le remplissage interne
@param ipadx Le remplissage interne dans la direction x
@param ipady Le remplissage interne dans la direction y
@return this Objet pour une future modification
*/
public GBC setIpad(int ipadx, int ipady)
{
this.ipadx = ipadx;
this.ipady = ipady;
return this;
}
}

ASTUCE
Certains concepteurs de GUI disposent doutils pour spcifier visuellement les contraintes (voir Figure 9.37) pour la
bote de dialogue de configuration dans NetBeans.

Figure 9.37
Spcifier des GridBagConstraints
dans NetBeans.

Livre Java .book Page 496 Jeudi, 25. novembre 2004 3:04 15

496

Au cur de Java 2 - Notions fondamentales

java.awt.GridBagConstraints 1.0

int gridx, gridy

Indique la colonne et la ligne de dpart de la cellule. Le paramtre par dfaut vaut 0.

int gridwidth, gridheight

Indique ltendue en colonnes et en lignes de la cellule. Le paramtre par dfaut vaut 1.

double weightx, weighty

Indique la capacit de la cellule sagrandir. Le paramtre par dfaut vaut 0.

int anchor

Indique lalignement du composant lintrieur de la cellule. Lune des positions absolues suivantes : CENTER, NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST ou NORTHWEST ou leurs
contreparties indpendantes de lorientation : FIRST_LINE_START, LINE_START, FIRST_LINE_END,
PAGE_START, CENTER, PAGE_END, LAST_LINE_START, LINE_END et LAST_LINE_END.

int fill

Indique le comportement de remplissage du composant lintrieur de la cellule. Lune des


constantes suivantes : NONE, BOTH, HORIZONTAL ou VERTICAL. Le paramtre par dfaut vaut
NONE.

int ipadx, ipady

Indique le remplissage interne du pourtour du composant. Le paramtre par dfaut vaut 0.

Insets insets

Indique le remplissage externe le long des limites de la cellule. Le paramtre par dfaut vaut
aucun remplissage.

GridBagConstraints(int gridx, int gridy, int gridwidth, int gridheight, double


weightx, double weighty, int anchor, int fill, Insets insets, int ipadx, int
ipady) 1.2

Construit un composant GridBagConstraints avec tous ses champs passs en paramtres. Sun
recommande de ne faire utiliser ce constructeur que par des gnrateurs de code automatique,
car il diminue la lisibilit du code.

SpringLayout
Depuis que les programmeurs ont rencontr GridBagLayout, ils ont pri lquipe Java de leur fournir un gestionnaire de mise en forme pareillement flexible mais plus intuitif. JDK 1.4 prsente le
SpringLayout. Dans cette section, vous verrez comment le mesurer.
SpringLayout permet dajouter des ressorts chaque composant. Un ressort est un appareil grce
auquel on peut spcifier des positions de composant. Comme indiqu la Figure 9.38, chaque
ressort dispose :
m

dune valeur minimale ;

dune valeur prfre ;

dune valeur maximale ;

dune valeur relle.

Livre Java .book Page 497 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

497

Lorsque le ressort est comprim ou tendu pendant la phase de mise en forme, sa valeur relle est
fixe ; elle est comprise entre la valeur minimale et la valeur maximale et sapproche de la valeur
prfre aussi prs que le permettent les autres ressorts. La valeur relle dtermine ensuite la position du
composant auquel il a t attach.
Figure 9.38

minimum

prfre

maximum

Un ressort.

valeur relle

La classe du ressort dfinit une opration de somme qui produit un nouveau ressort partir de deux
ressorts, lequel associe les caractristiques de chaque ressort. Lorsque vous disposez plusieurs
composants en ligne, vous attachez plusieurs ressorts, de sorte que leur somme stende sur la totalit du conteneur (voir Figure 9.39). Ce ressort de somme est maintenant compress ou tendu de
sorte que sa valeur gale la dimension du conteneur. Cette opration exerce une contrainte sur
chaque ressort. Chaque valeur de ressort est dfinie de manire que la contrainte de chaque ressort
gale la contrainte de la somme. Les valeurs de chaque ressort sont donc dtermines, et la disposition est fixe (si vous souhaitez connatre les dtails des calculs de contrainte, consultez la documentation API de la classe Spring).
Figure 9.39

Composants

conteneur

Somme de ressorts.

ressorts

Voyons un exemple simple. Supposons que vous vouliez disposer trois boutons horizontalement :
JButton b1 = new JButton("Yellow");
JButton b2 = new JButton("Blue");
JButton b3 = new JButton("Red");

Vous dfinissez dabord le gestionnaire de mise en forme du cadre sur un SpringLayout et ajoutez
les composants :
SpringLayout layout = new SpringLayout();
panel.setLayout(layout);

Livre Java .book Page 498 Jeudi, 25. novembre 2004 3:04 15

498

Au cur de Java 2 - Notions fondamentales

panel.add(b1);
panel.add(b2);
panel.add(b3);

Construisez maintenant un ressort avec une bonne compression. La mthode statique Spring.constant
produit un ressort avec des valeurs minimale, prfre et maximale donnes (le ressort nest pas
constant, il peut tre comprim ou tendu) :
Spring s = Spring.constant(0, 10000, 10000);

Attachez ensuite une copie du ressort du ct ouest du conteneur jusquau ct ouest de b1:
layout.putConstraint(SpringLayout.WEST, b1, s, SpringLayout.WEST, panel);

La mthode putConstraint ajoute le ressort donn, de sorte quil se termine au premier jeu de paramtres (le ct ouest de b1 dans notre cas) et commence au deuxime jeu de paramtres (le ct
ouest du panneau conteneur).
Vous liez ensuite les autres ressorts :
layout.putConstraint(SpringLayout.WEST, b2, s, SpringLayout.EAST, b1);
layout.putConstraint(SpringLayout.WEST, b3, s, SpringLayout.EAST, b2);

Enfin, vous accrochez un ressort au ct est du conteneur :


layout.putConstraint(SpringLayout.EAST, panel, s, SpringLayout.EAST, b3);

Rsultat : les quatre ressorts sont comprims la mme taille et les boutons, espacs de manire
gale (voir Figure 9.40).
Figure 9.40
Boutons lespacement
gal.

Vous pouvez aussi faire varier les distances. Supposons que vous vouliez installer une distance
fixe entre les boutons. Utilisez un Strut (un ressort qui ne peut pas tre tendu ni compress).
Pour obtenir ce ressort avec la version paramtre unique de la mthode Spring.constant,
inscrivez :
Spring strut = Spring.constant(10);

Si vous avez deux Struts entre les boutons, mais que vous laissiez les ressorts aux extrmits, le
rsultat est un groupe de boutons centr dans le conteneur (voir Figure 9.41).
Bien sr, la mise en forme du ressort nest pas vraiment ncessaire pour un agencement aussi simple.
Etudions un exemple plus complexe, une partie de la bote de dialogue de police de lexemple prcdent. Nous avons deux listes combines avec des intituls et voulons que les cts ouest des deux
listes combines commencent aprs le libell le plus long (voir Figure 9.42).

Livre Java .book Page 499 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

499

Figure 9.41
Ressorts et Struts.

Figure 9.42
Alignement de colonnes.

Ceci appelle une autre opration de ressort. Vous pouvez former un maximum de deux ressorts avec
la mthode statique Spring.max. Rsultat : un ressort aussi long que la plus longue des deux saisies.
Nous obtenons le maximum de deux cts est comme ceci :
Spring labelsEast = Spring.max(
layout.getConstraint(SpringLayout.EAST, faceLabel),
layout.getConstraint(SpringLayout.EAST, sizeLabel));

Notez que la mthode getConstraint produit un ressort qui part du ct ouest du conteneur pour
aboutir aux cts donns du composant.
Ajoutons un Strut de sorte quil y ait un espace entre les libells et les listes combines :
Spring combosWest = Spring.sum(labelsEast, strut);

Nous attachons maintenant ce ressort au ct ouest des deux listes combines. Le point de dpart est
le dpart du conteneur, car cest l que commence le ressort labelsEast:
layout.putConstraint(SpringLayout.WEST, face,
combosWest,SpringLayout.WEST, panel);
layout.putConstraint(SpringLayout.WEST, size,
combosWest,SpringLayout.WEST, panel);

Dsormais, les deux listes combines salignent car elles sont contenues dans le mme ressort.
Il existe toutefois un lger problme. Nous prfrerions que les intituls soient aligns droite.
Pour obtenir cet effet, il convient de trs bien comprendre les attachements de ressorts.
Etudions les ressorts horizontaux en dtail, car les ressorts verticaux suivent la mme logique.
La Figure 9.43 montre les trois manires dattacher les ressorts horizontaux :
m

relier le ct ouest du composant au ct ouest du composant ;

traverser la largeur du composant ;

Livre Java .book Page 500 Jeudi, 25. novembre 2004 3:04 15

500

Au cur de Java 2 - Notions fondamentales

relier le ct ouest du composant au ct est du composant.

Figure 9.43

container

Ressorts horizontaux
attachs un composant.

width
west
component

east

Vous rcuprez ces ressorts comme suit :


Spring west = layout.getConstraints(component).getX();
Spring width = layout.getConstraints(component).getWidth();
Spring east = layout.getConstraint(SpringLayout.EAST, component);

La mthode getConstraints produit un objet du type SpringLayout.Constraints. Il ressemble


un rectangle, mais les valeurs x, y, width et height sont des ressorts, et non des nombres. La
mthode getConstraint produit un seul ressort qui atteint lune des quatre limites du composant.
Vous pouvez aussi rcuprer le ressort ouest sous la forme
Spring west = layout.getConstraint(SpringLayout.WEST, component);

Bien sr, les trois ressorts sont lis : la somme des ressorts west et width doit tre gale east.
A la premire dfinition des contraintes du composant, la largeur est dfinie sur un ressort dont
les paramtres sont la largeur minimale, prfre et maximale du composant. Le ct ouest est
dfini sur 0.
ATTENTION
Si vous ne dfinissez pas le ressort ouest (et nord) dun composant, le composant sarrte au dcalage 0 du conteneur.

Si un composant possde deux jeux de ressorts et que vous en ajoutiez un troisime, il devient
surcontraint (overconstrained). Lun des ressorts existants est alors supprim et sa valeur est
calcule comme la somme ou la diffrence des autres ressorts. Le Tableau 9.3 montre le ressort
recalcul.

Livre Java .book Page 501 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

501

Tableau 9.3 : Ajouter un ressort un composant surcontraint

Ressort ajout

Ressort supprim

Remplac par

west

width

east west

width

east

west + width

east

west

east width

INFO
La diffrence entre deux ressorts peut ne pas tre intuitive, mais elle est logique dans lalgbre des ressorts. Il nexiste
pas de mthode Java pour la soustraction du ressort. Si vous devez calculer la diffrence entre deux ressorts, utilisez
Spring.sum(s, Spring.minus(t))

Vous en savez maintenant suffisamment sur les ressorts pour rsoudre le problme de "lalignement
droite". Calculez le maximum des largeurs des deux intituls. Dfinissez ensuite le ressort est des
deux intituls sur ce maximum. Comme vous pouvez le voir dans le Tableau 9.3, les largeurs dintituls ne changent pas, les ressorts ouest sont recalculs et les intituls salignent sur la limite est :
Spring labelsEast = Spring.sum(strut,
Spring.max(layout.getConstraints(faceLabel).getWidth(),
Spring.max(layout.getConstraints(sizeLabel).getWidth()));
layout.putConstraint(SpringLayout.EAST, faceLabel, labelsEast,
SpringLayout.WEST, panel);
layout.putConstraint(SpringLayout.EAST, sizeLabel, labelsEast,
SpringLayout.WEST, panel);

LExemple 9.16 montre comment disposer la bote de dialogue de police avec des ressorts. Si vous
regardez le code, vous conviendrez certainement que la disposition du ressort est moins intuitive que
celle de GridBagLayout. Nous esprons que des outils seront bientt conus pour rendre plus abordable la disposition des ressorts. Entre-temps, nous vous conseillons de vous en tenir au GridBagLayout pour les mises en formes complexes.
Exemple 9.16 : SpringLayoutTest.java
import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;

public class SpringLayoutTest


{
public static void main(String[] args)
{
FontDialogFrame frame = new FontDialogFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre qui utilise une mise en forme de ressort pour agencer
les composants de slection de police.

Livre Java .book Page 502 Jeudi, 25. novembre 2004 3:04 15

502

Au cur de Java 2 - Notions fondamentales

*/
class FontDialogFrame extends JFrame
{
public FontDialogFrame()
{
setTitle("FontDialog");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
JPanel panel = new JPanel();
SpringLayout layout = new SpringLayout();
panel.setLayout(layout);
ActionListener listener = new FontAction();
// construire les composants
JLabel faceLabel = new JLabel("Font Face: ");
face = new JComboBox(new String[]
{
"Serif", "SansSerif", "Monospaced",
"Dialog", "DialogInput"
});
face.addActionListener(listener);
JLabel sizeLabel = new JLabel("Size: ");
size = new JComboBox(new String[]
{
"8", "10", "12", "15", "18", "24", "36", "48"
});
size.addActionListener(listener);
bold = new JCheckBox("Bold");
bold.addActionListener(listener);
italic = new JCheckBox("Italic");
italic.addActionListener(listener);
sample = new JTextArea();
sample.setText("The quick brown fox jumps over the lazy dog");
sample.setEditable(false);
sample.setLineWrap(true);
sample.setBorder(BorderFactory.createEtchedBorder());
panel.add(faceLabel);
panel.add(sizeLabel);
panel.add(face);
panel.add(size);
panel.add(bold);
panel.add(italic);
panel.add(sample);

Livre Java .book Page 503 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

503

// ajouter des ressorts aux composants de mise en forme


Spring strut = Spring.constant(10);
Spring labelsEast = Spring.sum(strut,
Spring.max(
layout.getConstraints(faceLabel).getWidth(),
layout.getConstraints(sizeLabel).getWidth()));
layout.putConstraint(SpringLayout.EAST, faceLabel, labelsEast,
SpringLayout.WEST, panel);
layout.putConstraint(SpringLayout.EAST, sizeLabel, labelsEast,
SpringLayout.WEST, panel);
layout.putConstraint(SpringLayout.NORTH, faceLabel, strut,
SpringLayout.NORTH, panel);
layout.putConstraint(SpringLayout.NORTH, face, strut,
SpringLayout.NORTH, panel);
Spring secondRowNorth = Spring.sum(strut,
Spring.max(
layout.getConstraint(SpringLayout.SOUTH, faceLabel),
layout.getConstraint(SpringLayout.SOUTH, face)));
layout.putConstraint(SpringLayout.NORTH, sizeLabel, secondRowNorth,
SpringLayout.NORTH,
panel);
layout.putConstraint(SpringLayout.NORTH, size, secondRowNorth,
SpringLayout.NORTH, panel);
layout.putConstraint(SpringLayout.WEST, face, strut, SpringLayout.EAST,
faceLabel);
layout.putConstraint(SpringLayout.WEST, size, strut, SpringLayout.EAST,
sizeLabel);
layout.putConstraint(SpringLayout.WEST, bold, strut, SpringLayout.WEST,
panel);
layout.putConstraint(SpringLayout.WEST, italic, strut,
SpringLayout.WEST, panel);
Spring s = Spring.constant(10, 10000, 10000);
Spring thirdRowNorth = Spring.sum(s,
Spring.max(
layout.getConstraint(SpringLayout.SOUTH, sizeLabel),
layout.getConstraint(SpringLayout.SOUTH, size)));
layout.putConstraint(SpringLayout.NORTH, bold, thirdRowNorth,
SpringLayout.NORTH, panel);
layout.putConstraint(SpringLayout.NORTH, italic, s, SpringLayout.SOUTH,
bold);
layout.putConstraint(SpringLayout.SOUTH, panel, s, SpringLayout.SOUTH,
italic);

Livre Java .book Page 504 Jeudi, 25. novembre 2004 3:04 15

504

Au cur de Java 2 - Notions fondamentales

Spring secondColumnWest = Spring.sum(strut,


Spring.max(
layout.getConstraint(SpringLayout.EAST, face),
layout.getConstraint(SpringLayout.EAST, size)));
layout.putConstraint(SpringLayout.WEST, sample, secondColumnWest,
SpringLayout.WEST, panel);
layout.putConstraint(SpringLayout.SOUTH, sample, Spring.minus(strut),
SpringLayout.SOUTH,
panel);
layout.putConstraint(SpringLayout.NORTH, sample, strut,
SpringLayout.NORTH, panel);
layout.putConstraint(SpringLayout.EAST, panel, strut, SpringLayout.EAST,
sample);
add(panel);
}
public static final int DEFAULT_WIDTH = 400;
public static final int DEFAULT_HEIGHT = 200;
private
private
private
private
private

JComboBox
JComboBox
JCheckBox
JCheckBox
JTextArea

face;
size;
bold;
italic;
sample;

/**
Un couteur daction qui modifie la police du
texte dexemple.
*/
private class FontAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String fontFace = (String) face.getSelectedItem();
int fontStyle = (bold.isSelected()? Font.BOLD: 0)
+ (italic.isSelected()? Font.ITALIC: 0);
int fontSize = Integer.parseInt((String) size.getSelectedItem());
Font font = new Font(fontFace, fontStyle, fontSize);
sample.setFont(font);
sample.repaint();
}
}
}
javax.swing.SpringLayout 1.4

SpringLayout.Constraints getConstraints(Component c)

Rcupre les contraintes du composant donn.


Paramtre :

Lun des composants du conteneur gr par ce gestionnaire


de mise en forme.

void putConstraint(String endSide, Component end, Spring s, String startSide,


Component start)

Livre Java .book Page 505 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

505

void putConstraint(String endSide, Component end, int pad, String startSide,


Component start)

Dfinissent le ct donn du composant end sur un ressort obtenu par lajout du ressort s, ou un
strut avec la taille pad, au ressort qui va de lextrmit gauche du conteneur au ct donn du
conteneur start.
Paramtres :

endSide, startSide Lun de WEST, EAST, NORTH ou SOUTH.


end

Le composant auquel un ressort est ajout.

Lune des commandes dajout du ressort ajout.

pad

La taille de la commande dajout Strut.

start

Le composant quatteint lautre ressort de commande


dajout.

javax.swing.SpringLayout.Constraints 1.4

Constraints(Component c) 5.0

Construit un objet Constraints dont les positions, la largeur et les ressorts correspondent au
composant donn.

Spring getX()
Spring getY()

Ces mthodes renvoient le ressort qui part du dbut du conteneur jusqu louest ou jusquau
nord du composant contraint.

Spring getWidth()
Spring getHeight()

Ces mthodes renvoient le ressort qui stire sur la largeur ou la hauteur du composant contraint.

Spring getConstraint(String side)


void setConstraint(String edge, Spring s)

Ces mthodes rcuprent ou dfinissent un ressort partant du dbut du conteneur jusquau ct


donn du composant contraint.
Paramtres :

side

Lune des constantes WEST, EAST, NORTH ou SOUTH de la


classe SpringLayout.

Le ressort dfinir.

javax.swing.Spring 1.4

static Spring constant(int preferred)

Construit un Strut avec la taille prfre donne. Les tailles minimale et maximale sont dfinies
sur la taille prfre.

static Spring constant(int minimum, int preferred, int maximum)

Construit un ressort avec les tailles minimale, prfre et maximale donnes.

static Spring sum(Spring s, Spring t)

Renvoie la somme de ressort de s et t.

static Spring max(Spring s, Spring t)

Renvoie le ressort maximal de s et t.

Livre Java .book Page 506 Jeudi, 25. novembre 2004 3:04 15

506

Au cur de Java 2 - Notions fondamentales

static Spring minus(Spring s)

Renvoie loppos du ressort s.

static Spring scale(Spring s, float factor) 5.0

Echelonne les tailles minimale, prfre et maximale de s du facteur donn. Si le facteur est
ngatif, loppos chelonn de s est renvoy.

static Spring width(Component c) 5.0


static Spring height(Component c) 5.0

Renvoient un ressort dont les tailles minimale, prfre et maximale sont gales aux largeurs ou
hauteurs minimale, prfres et maximale du composant donn.

int getMinimumValue()
int getPreferredValue()
int getMaximumValue()

Ces mthodes renvoient la valeur minimale, prfre et maximale de ce ressort.

int getValue()
void setValue(int newValue)

Ces mthodes rcuprent et dfinissent la valeur du ressort. Lors de la dfinition de la valeur


dune chane compose, les valeurs des composants sont aussi dfinies.

Cration sans gestionnaire de mise en forme


Vous souhaiterez parfois ne pas utiliser de gestionnaire de mise en forme et pouvoir placer un
composant un endroit fixe (appel position absolue). Pour des applications indpendantes de la
plate-forme, ce nest pas une trs bonne ide. En revanche, elle est adapte llaboration rapide
dun prototype.
Voici comment procder pour placer un composant un endroit fixe :
1. Dfinissez le gestionnaire de mise en forme avec la valeur null.
2. Ajoutez le composant de votre choix dans le conteneur.
3. Spcifiez ensuite la position et la taille voulues :
setLayout(null);
JButton ok = new JButton("OK");
add(ok);
ok.setBounds(10, 10, 30, 15);
java.awt.Component 1.0

void setBounds(int x, int y, int width, int height)

Dplace et redimensionne un composant.


Paramtres :

x, y

Le nouveau coin suprieur gauche du composant.

width, height

La nouvelle taille du composant.

Livre Java .book Page 507 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

507

Gestionnaires de mise en forme personnaliss


En principe, vous avez la possibilit de concevoir votre propre classe LayoutManager pour grer les
composants de faon spciale. Par exemple, vous pourriez organiser tous les composants dun conteneur de faon former un cercle. Bien que cela ncessite presque toujours beaucoup de travail et de
temps, le rsultat est assez tonnant, comme le prouve la Figure 9.44.
Figure 9.44
Mise en forme circulaire.

Pour disposer de votre propre gestionnaire de mise en forme, celui-ci doit implmenter linterface
LayoutManager. Vous devez redfinir les cinq mthodes suivantes :
void addLayoutComponent(String s, Component c);
void removeLayoutComponent(Component c);
Dimension preferredLayoutSize(Container parent);
Dimension minimumLayoutSize(Container parent);
void layoutContainer(Container parent);

Les deux premires mthodes sont appeles pour, respectivement, ajouter ou supprimer un composant. Si vous ne conservez aucune information supplmentaire sur les composants, vous pouvez faire
en sorte quelles nexcutent aucune action. Les deux mthodes suivantes calculent lespace prfr
et minimal pour lorganisation des composants. Ces deux valeurs sont gnralement quivalentes.
La cinquime mthode effectue toute la mise en forme et invoque la mthode setBounds sur tous les
composants.
INFO
AWT possde une seconde interface appele LayoutManager2 comprenant dix mthodes implmenter au lieu de
cinq. Son objectif est de permettre lutilisateur demployer la mthode add avec des contraintes. Par exemple, les
gestionnaires BorderLayout et GridBagLayout implmentent cette interface.

LExemple 9.17 est une implmentation simple du gestionnaire CircleLayout, qui aligne les
composants le long dune ellipse au sein du conteneur parent.
Exemple 9.17 : CircleLayoutTest.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CircleLayoutTest
{
public static void main(String[] args)
{

Livre Java .book Page 508 Jeudi, 25. novembre 2004 3:04 15

508

Au cur de Java 2 - Notions fondamentales

CircleLayoutFrame frame = new CircleLayoutFrame();


frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
/**
Un cadre affichant des boutons disposs en cercle.
*/
class CircleLayoutFrame extends JFrame
{
public CircleLayoutFrame()
{
setTitle("CircleLayoutTest");
setLayout(new CircleLayout());
add(new JButton("Yellow"));
add(new JButton("Blue"));
add(new JButton("Red"));
add(new JButton("Green"));
add(new JButton("Orange"));
add(new JButton("Fuschia"));
add(new JButton("Indigo"));
}
}
/**
Un gestionnaire de mise en forme disposant les composants
en cercle.
*/
class CircleLayout implements LayoutManager
{
public void addLayoutComponent(String name,
Component comp)
{}
public void removeLayoutComponent(Component comp)
{}
public void setSizes(Container parent)
{
if (sizesSet) return;
int n = parent.getComponentCount();
preferredWidth = 0;
preferredHeight = 0;
minWidth = 0;
minHeight = 0;
maxComponentWidth = 0;
maxComponentHeight = 0;
//
//
//
for (int
{

calculer les largeur et hauteur maxi de composants


et affecter la somme des tailles des composants
la taille prfre
i = 0; i < n; i++)
Component c = parent.getComponent(i);
if (c.isVisible())

Livre Java .book Page 509 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

{
Dimension d = c.getPreferredSize();
maxComponentWidth = Math.max(maxComponentWidth,
d.width);
maxComponentHeight = Math.max(maxComponentHeight,
d.height);
preferredWidth += d.width;
preferredHeight += d.height;
}
}
minWidth = preferredWidth / 2;
minHeight = preferredHeight / 2;
sizesSet = true;
}
public Dimension preferredLayoutSize(Container parent)
{
setSizes(parent);
Insets insets = parent.getInsets();
int width = preferredWidth+insets.left
+insets.right;
int height = preferredHeight+insets.top
+insets.bottom;
return new Dimension(width, height);
}
public Dimension minimumLayoutSize(Container parent)
{
setSizes(parent);
Insets insets = parent.getInsets();
int width = minWidth+insets.left+insets.right;
int height = minHeight+insets.top+insets.bottom;
return new Dimension(width, height);
}
public void layoutContainer(Container parent)
{
setSizes(parent);
// calculer le centre du cercle
Insets insets = parent.getInsets();
int containerWidth = parent.getSize().width
- insets.left - insets.right;
int containerHeight = parent.getSize().height
- insets.top - insets.bottom;
int xcenter = insets.left+containerWidth / 2;
int ycenter = insets.top+containerHeight / 2;
// calculer le rayon du cercle
int xradius = (containerWidth - maxComponentWidth) / 2;
int yradius = (containerHeight - maxComponentHeight) / 2;
int radius = Math.min(xradius, yradius);
// disposer les composants le long du cercle
int n = parent.getComponentCount();

509

Livre Java .book Page 510 Jeudi, 25. novembre 2004 3:04 15

510

Au cur de Java 2 - Notions fondamentales

for (int i = 0; i < n; i++)


{
Component c = parent.getComponent(i);
if (c.isVisible())
{
double angle = 2 * Math.PI * i / n;
// centre du composant
int x = xcenter+ (int)(Math.cos(angle) * radius);
int y = ycenter+ (int)(Math.sin(angle) * radius);
// dplacer le composant pour que son centre soit (x, y)
// et sa taille gale sa taille prfre
Dimension d = c.getPreferredSize();
c.setBounds(x - d.width / 2, y - d.height / 2,
d.width, d.height);
}
}
}
private
private
private
private
private
private
private

int minWidth = 0;
int minHeight = 0;
int preferredWidth = 0;
int preferredHeight = 0;
boolean sizesSet = false;
int maxComponentWidth = 0;
int maxComponentHeight = 0;

}
java.awt.LayoutManager 1.0

void addLayoutComponent(String name, Component component)

Ajoute un composant la mise en forme.


Paramtres :

name

Un identifiant pour le placement du composant.

component

Le composant ajouter.

void removeLayoutComponent(Component component)

Supprime un composant de la mise en forme.


Paramtres :

component

Le composant supprimer.

Dimension preferredLayoutSize(Container parent)

Renvoie la taille prfre du conteneur pour cette mise en forme.


Paramtres :

parent

Le conteneur dont les composants sont mis en forme.

Dimension minimumLayoutSize(Container parent)

Renvoie la taille minimale du conteneur pour cette mise en forme.


Paramtres :

parent

Le conteneur dont les composants sont mis en forme.

void layoutContainer(Container parent)

Organise les composants dans un conteneur.


Paramtres :

parent

Le conteneur dont les composants sont mis en forme.

Livre Java .book Page 511 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

511

Squence de tabulation
Lorsque vous ajoutez de nombreux composants dans une fentre, vous devez penser les ordonner
selon une squence de tabulation. Lorsquune fentre est affiche pour la premire fois, le premier
composant de la squence reoit le focus dentre. Chaque fois que lutilisateur appuie sur la touche
de tabulation, le focus se dplace sur le composant suivant dans la squence de tabulation. Rappelezvous que les composants qui reoivent le focus peuvent tre manipuls laide du clavier. Par exemple, un bouton ayant reu le focus dentre peut tre "cliqu" au moyen de la barre despacement.
Mme si vous nutilisez pas personnellement la touche de tabulation pour naviguer dans un ensemble de contrles, sachez quelle sert de nombreux utilisateurs. Parmi eux, on trouve ceux qui sont
allergiques la souris, ou ceux qui ne peuvent tout simplement pas lutiliser en raison dun handicap
ou bien parce quils commandent linterface utilisateur par la voix. Vous devez par consquent savoir
de quelle manire Swing gre la squence de tabulation.
La squence de tabulation se fait de faon classique, cest--dire dabord de gauche droite puis de
haut en bas. Par exemple, dans la bote de dialogue des polices, la squence de tabulation est la
suivante (voir Figure 9.45) :
1. Zone de liste droulante Face.
2. Zone de texte pour lchantillon de police (appuyez sur Ctrl+Tab pour passer au champ suivant ;
la tabulation est considre comme une entre de texte).
3. Zone de liste droulante Size.
4. Case cocher Bold.
5. Case cocher Italic.
Figure 9.45
Squence
de tabulation.

INFO
Dans lancienne version dAWT, lordre selon lequel les composants taient insrs dans le conteneur dterminait la
squence de tabulation. Dans Swing, lordre dinsertion na aucune importance, seule la disposition des composants
est prise en compte.

La situation est encore plus complexe si un conteneur en contient dautres. Lorsque le focus dentre
est transmis un autre conteneur, il est reu par le composant situ en haut gauche dans ce conteneur. Il passe ensuite par tous les autres composants du conteneur. Enfin, le focus est transmis au
composant qui suit le conteneur dans la squence de tabulation.

Livre Java .book Page 512 Jeudi, 25. novembre 2004 3:04 15

512

Au cur de Java 2 - Notions fondamentales

Vous pouvez exploiter cette fonctionnalit en regroupant des lments apparents dans un autre
conteneur, comme un panneau.
INFO
Depuis, le JDK 1.4, vous appelez
component.setFocusable(false);

pour supprimer un composant de la squence de tabulation. Dans les anciennes versions, vous deviez remplacer la
mthode isFocusTraversable, mais cette mthode est aujourdhui dprcie.

En rsum, il existe deux politiques de tabulation standard dans le JDK 1.4 :


m

Les applications AWT pures utilisent DefaultFocusTraversalPolicy. Les composants sont


inclus dans la squence de tabulation sils sont visibles, affichables, activs et peuvent avoir le
focus et si leurs pairs natifs peuvent avoir le focus. Les composants sont tabuls dans lordre de
leur insertion dans le conteneur.

Les applications Swing utilisent LayoutFocusTraversalPolicy. Les composants sont inclus


dans la squence de focus sils sont visibles, affichables, activs et peuvent avoir le focus. Les
composants sont tabuls dans lordre gomtrique : de gauche droite, puis de haut en bas.
Toutefois, un conteneur introduit un nouveau "cycle" : ses composants sont tabuls dabord
avant que le successeur du conteneur ne reoive le focus.
INFO

La notion de "cycle" est un peu droutante. Aprs avoir atteint le dernier lment dun conteneur enfant, le focus
ne revient pas au premier lment, il passe au successeur du conteneur. LAPI prend en charge de vrais cycles, comprenant des frappes au clavier qui montent et descendent dans une hirarchie de cycle. Toutefois, la politique standard
de tabulation nutilise pas les cycles hirarchiques. Elle aplatit la hirarchie du cycle dans une tabulation linaire
(depth-first).

INFO
Dans le JDK 1.3, vous pouviez modifier lordre de tabulation par dfaut en appelant la mthode setNextFocusableComponent de la classe JComponent. Cette mthode est maintenant dprcie. Pour modifier lordre de tabulation, essayez de grouper des composants lis dans des panneaux, de sorte quils forment des cycles. Si cela ne
fonctionne pas, vous devez installer un comparateur qui trie diffremment les composants ou remplace totalement
la politique de tabulation. Aucune de ces oprations ne semble avoir pour objet la pusillanimit (voyez la documentation Sun API pour en savoir plus).

Botes de dialogue
Jusqu prsent, tous les composants dinterface que nous avons vus figuraient lintrieur dun
cadre cr dans un programme. Cest la situation la plus courante lorsque vous crivez des applets
destins tre excuts dans un navigateur Web. Mais si vous crivez des applications, vous prfrerez utiliser des botes de dialogue spares qui safficheront pour proposer des informations lutilisateur ou en recevoir de sa part.

Livre Java .book Page 513 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

513

A limage de la plupart des systmes de fentrage, AWT fait la distinction entre les botes de dialogue
modales et non modales. Une bote de dialogue modale ne permet pas lutilisateur dinteragir avec
dautres fentres de lapplication tant quelle demeure ouverte. Vous lutilisez lorsque vous avez besoin de
rcuprer des informations de la part de lutilisateur avant de poursuivre lexcution du programme. Par
exemple, quand lutilisateur demande lire un fichier, une bote de dialogue modale apparat, lui demandant de spcifier le nom du fichier pour que le programme puisse entreprendre une opration de lecture.
Lapplication ne peut continuer sexcuter que lorsque lutilisateur a ferm la bote de dialogue.
Une bote de dialogue non modale autorise lutilisateur interagir avec les autres fentres de lapplication
alors quelle est ouverte. La bote de dialogue Rechercher/Remplacer en est un exemple. Elle peut rester
apparente aussi longtemps que ncessaire pendant que lutilisateur manipule les diffrentes fentres.
Nous allons commencer par ltude dune bote de dialogue modale simple contenant un seul
message. Swing dispose dune classe trs utile, JOptionPane, qui vous permet dafficher une bote
de dialogue standard sans avoir crire de code spcifique. Vous verrez ensuite comment crer des
botes de dialogue plus complexes en implmentant les vtres. Pour finir, vous tudierez lchange
des donnes entre une application et une bote de dialogue.
Nous conclurons cette section par la prsentation de deux botes de dialogue standard : Fichier et
Couleurs. Elles sont complexes, et si vous souhaitez crire les vtres, il faudra absolument vous
familiariser avec la classe JFileChooser de Swing. La bote de dialogue JColorChooser permet
un utilisateur de choisir une couleur partir dune palette.

Botes de dialogue doptions


Swing propose un ensemble de botes de dialogue standard prtes lemploi, qui permettent de
demander lutilisateur de fournir une information. La classe JOptionPane propose quatre mthodes
static pour afficher ces botes de dialogue :
showMessageDialog
showConfirmDialog
showOptionDialog
showInputDialog

Affiche un message et attend que lutilisateur clique sur OK.


Affiche un message et reoit une confirmation de lutilisateur (comme
OK/Annuler).
Affiche un message et rcupre loption choisie parmi plusieurs.
Affiche un message et rcupre une ligne entre par lutilisateur.

La Figure 9.46 prsente une bote de dialogue standard. Comme vous pouvez le voir, elle contient les
composants suivants :
m

une icne ;

un message ;

un ou plusieurs boutons doption.

Figure 9.46
Une bote de dialogue
doptions.

Livre Java .book Page 514 Jeudi, 25. novembre 2004 3:04 15

514

Au cur de Java 2 - Notions fondamentales

La bote de dialogue de saisie comprend un composant supplmentaire pour recueillir lentre de


lutilisateur. Il peut sagir dun champ de texte dans lequel il tape une chane quelconque, ou dune
zone de liste droulante dans laquelle il slectionne une valeur.
La mise en forme exacte de ces botes de dialogue et le choix des icnes pour les types de messages
standard dpendent du style dinterface look and feel implment.
Licne situe gauche dpend du type de message. Il en existe cinq :
ERROR_MESSAGE
INFORMATION_MESSAGE
WARNING_MESSAGE
QUESTION_MESSAGE
PLAIN_MESSAGE

Le type PLAIN_MESSAGE ne possde pas dicne. Pour chaque type de bote de dialogue, il existe une
mthode vous permettant de fournir votre propre icne.
Vous pouvez spcifier un message pour chaque type de bote de dialogue. Ce message peut tre une
chane, une icne, un composant dinterface utilisateur ou tout autre objet. Voici comment est affich
lobjet message :
String
Icon
Component
Object[]
tout autre objet

La chane est affiche.


Licne est affiche.
Le composant est affich.
Tous les objets du tableau sont affichs les uns au-dessus des autres.
La mthode toString est applique et la chane rsultante est affiche.

Pour obtenir un aperu de ces options, excutez le programme de lExemple 9.18.


Bien sr, le message le plus frquent est celui de type chane. Un objet Component offre une
souplesse optimale puisque vous pouvez utiliser la mthode paintComponent pour dessiner tout ce
que vous souhaitez.
Les boutons situs en bas dune bote de dialogue dpendent de son type et du type doption. Les
mthodes showMessageDialog et showInputDialog implmentent uniquement un jeu standard de
boutons (respectivement OK et OK/Cancel). La mthode showConfirmMessage vous permet de choisir
parmi les quatre options suivantes :
DEFAULT_OPTION
YES_NO_OPTION
YES_NO_CANCEL_OPTION
OK_CANCEL_OPTION

Avec la mthode showOptionDialog, vous pouvez spcifier un ensemble arbitraire doptions en


fournissant un tableau dobjets. Chaque lment du tableau est trait de la faon suivante :
String
Icon
Component
tout autre objet

Un bouton est cr avec la chane comme libell.


Un bouton est cr avec licne comme libell.
Le composant est affich.
La mthode toString est applique et un bouton est cr avec pour
libell la chane rsultante.

Livre Java .book Page 515 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

515

Voici les valeurs renvoyes par ces mthodes :


showMessageDialog
showConfirmDialog
showOptionDialog
showInputDialog

Aucune.
Un entier reprsentant loption choisie.
Un entier reprsentant loption choisie.
La chane entre ou slectionne par lutilisateur.

Les mthodes showConfirmDialog et showOptionDialog renvoient des entiers pour indiquer le


bouton que lutilisateur a slectionn. Pour une bote de dialogue doptions, il sagit de lindice de
llment choisi ou de la valeur CLOSED_OPTION si lutilisateur a ferm la bote de dialogue au lieu
de faire une slection. Pour une bote de dialogue de confirmation, la valeur renvoye peut tre lune
des suivantes :
OK_OPTION
CANCEL_OPTION
YES_OPTION
NO_OPTION
CLOSED_OPTION

La diversit des choix peut sembler dconcertante, mais en pratique cest trs simple :
1. Choisissez le type de bote de dialogue (message, confirmation, options ou entre).
2. Choisissez licne (erreur, information, avertissement, question, aucune ou personnalise).
3. Choisissez le message (chane, icne, composant personnalis ou un tableau dobjets).
4. Pour une bote de dialogue de confirmation, choisissez un type doption pour le jeu de boutons
(default, Yes/No, Yes/No/Cancel ou OK/Cancel).
5. Pour une bote de dialogue doptions, choisissez les options (chane, icne, composant personnalis)
et loption par dfaut.
6. Pour une bote de dialogue de saisie, choisissez entre un champ de texte et une zone de liste
droulante.
7. Dterminez la mthode approprie appeler dans lAPI JOptionPane.
Supposons par exemple, que vous vouliez afficher la bote de dialogue de la Figure 9.46. Elle
contient un message et invite lutilisateur confirmer ou annuler. Par consquent, il sagit dune
bote de dialogue de confirmation. Licne correspond un message de type avertissement, le
message est une chane et le jeu de boutons est OK_CANCEL_OPTION. Voici lappel que vous devez
excuter :
int selection = JOptionPane.showConfirmDialog(parent,
"Message", "Title",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.WARNING_MESSAGE);
if (selection == JOptionPane.OK_OPTION) . . .

ASTUCE
Une chane de message peut contenir des caractres de fin de ligne (\n). Laffichage est alors rparti sur plusieurs
lignes.

Livre Java .book Page 516 Jeudi, 25. novembre 2004 3:04 15

516

Au cur de Java 2 - Notions fondamentales

Le programme de lExemple 9.18 vous permet deffectuer votre slection (voir Figure 9.47). Il affiche
ensuite la bote de dialogue qui en rsulte.
Figure 9.47
Le programme
OptionDialogTest.

Exemple 9.18 : OptionDialogTest.java


import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.awt.geom.*;
java.util.*;
javax.swing.*;
javax.swing.border.*;

public class OptionDialogTest


{
public static void main(String[] args)
{
OptionDialogFrame frame = new OptionDialogFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un panneau contenant des boutons radio
dans une zone encadre avec un titre.
*/
class ButtonPanel extends JPanel
{
/**
Construit un panneau avec des boutons.
@param title Le titre du panneau
@param options Un tableau de libells de boutons radio
*/
public ButtonPanel(String title, String[] options)
{
setBorder(BorderFactory.createTitledBorder
(BorderFactory.createEtchedBorder(), title));
setLayout(new BoxLayout(this,

Livre Java .book Page 517 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

BoxLayout.Y_AXIS));
group = new ButtonGroup();
// crer un bouton radio pour chaque option
for (int i = 0; i < options.length; i++)
{
JRadioButton b = new JRadioButton(options[i]);
b.setActionCommand(options[i]);
add(b);
group.add(b);
b.setSelected(i == 0);
}
}
/**
Extrait loption slectionne.
@return Le libell du bouton radio slectionn.
*/
public String getSelection()
{
return group.getSelection().getActionCommand();
}
private ButtonGroup group;
}
/**
Un cadre contenant des paramtres pour la slection
de diffrentes botes de dialogue doptions.
*/
class OptionDialogFrame extends JFrame
{
public OptionDialogFrame()
{
setTitle("OptionDialogTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
JPanel gridPanel = new JPanel();
gridPanel.setLayout(new GridLayout(2, 3));
typePanel = new ButtonPanel("Type",
new String[]
{
"Message",
"Confirm",
"Option",
"Input"
});
messageTypePanel = new ButtonPanel("Message type",
new String[]
{
"ERROR_MESSAGE",
"INFORMATION_MESSAGE",
"WARNING_MESSAGE",
"QUESTION_MESSAGE",
"PLAIN_MESSAGE"
});

517

Livre Java .book Page 518 Jeudi, 25. novembre 2004 3:04 15

518

Au cur de Java 2 - Notions fondamentales

messagePanel = new ButtonPanel("Message",


new String[]
{
"String",
"Icon",
"Component",
"Other",
"Object"
});
optionTypePanel = new ButtonPanel("Confirm",
new String[]
{
"DEFAULT_OPTION",
"YES_NO_OPTION",
"YES_NO_CANCEL_OPTION",
"OK_CANCEL_OPTION"
});
optionsPanel = new ButtonPanel("Option",
new String[]
{
"String[]",
"Icon[]",
"Object[]"
});
inputPanel = new ButtonPanel("Input",
new String[]
{
"Text field",
"Combo box"
});
gridPanel.add(typePanel);
gridPanel.add(messageTypePanel);
gridPanel.add(messagePanel);
gridPanel.add(optionTypePanel);
gridPanel.add(optionsPanel);
gridPanel.add(inputPanel);
// ajouter un panneau avec un bouton Afficher (Show)
JPanel showPanel = new JPanel();
JButton showButton = new JButton("Show");
showButton.addActionListener(new ShowAction());
showPanel.add(showButton);
add(gridPanel, BorderLayout.CENTER);
add(showPanel, BorderLayout.SOUTH);
}
/**
Rcupre le message slectionn.
@return Une chane, une icne, un composant ou un tableau
dobjets, selon la slection dans le panneau Message

Livre Java .book Page 519 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

*/
public Object getMessage()
{
String s = messagePanel.getSelection();
if (s.equals("String"))
return messageString;
else if (s.equals("Icon"))
return messageIcon;
else if (s.equals("Component"))
return messageComponent;
else if (s.equals("Object[]"))
return new Object[]
{
messageString,
messageIcon,
messageComponent,
messageObject
};
else if (s.equals("Other"))
return messageObject;
else return null;
}
/**
Rcupre les options actuellement slectionnes
@return Un tableau de chanes, dicnes ou dobjets,
selon la slection dans le panneau Option
*/
public Object[] getOptions()
{
String s = optionsPanel.getSelection();
if (s.equals("String[]"))
return new String[] { "Yellow", "Blue", "Red" };
else if (s.equals("Icon[]"))
return new Icon[]
{
new ImageIcon("yellow-ball.gif"),
new ImageIcon("blue-ball.gif"),
new ImageIcon("red-ball.gif")
};
else if (s.equals("Object[]"))
return new Object[]
{
messageString,
messageIcon,
messageComponent,
messageObject
};
else
return null;
}
/**
Rcupre le message ou le type doption slectionn
@param panel Le panneau Message Type ou Confirm
@return La constante XXX_MESSAGE ou XXX_OPTION
de la classe JOptionPane

519

Livre Java .book Page 520 Jeudi, 25. novembre 2004 3:04 15

520

Au cur de Java 2 - Notions fondamentales

*/
public int getType(ButtonPanel panel)
{
String s = panel.getSelection();
try
{
return JOptionPane.class.getField(s).getInt(null);
}
catch(Exception e)
{
return -1;
}
}
/**
Lcouteur daction pour le bouton Show affiche une
bote de dialogue Confirm, Input, Message ou Option
selon la slection dans le panneau Type.
*/
private class ShowAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
if (typePanel.getSelection().equals("Confirm"))
JOptionPane.showConfirmDialog(
OptionDialogFrame.this,
getMessage(),
"Title",
getType(optionTypePanel),
getType(messageTypePanel));
else if (typePanel.getSelection().equals("Input"))
{
if (inputPanel.getSelection().equals("Text field"))
JOptionPane.showInputDialog(
OptionDialogFrame.this,
getMessage(),
"Title",
getType(messageTypePanel));
else
JOptionPane.showInputDialog(
OptionDialogFrame.this,
getMessage(),
"Title",
getType(messageTypePanel),
null,
new String[] { "Yellow", "Blue", "Red" },
"Blue");
}
else if (typePanel.getSelection().equals("Message"))
JOptionPane.showMessageDialog(
OptionDialogFrame.this,
getMessage(),
"Title",
getType(messageTypePanel));
else if (typePanel.getSelection().equals("Option"))
JOptionPane.showOptionDialog(
OptionDialogFrame.this,
getMessage(),
"Title",

Livre Java .book Page 521 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

521

getType(optionTypePanel),
getType(messageTypePanel),
null,
getOptions(),
getOptions()[0]);
}
}
public static final int DEFAULT_WIDTH = 600;
public static final int DEFAULT_HEIGHT = 400;
private
private
private
private
private
private

ButtonPanel
ButtonPanel
ButtonPanel
ButtonPanel
ButtonPanel
ButtonPanel

typePanel;
messagePanel;
messageTypePanel;
optionTypePanel;
optionsPanel;
inputPanel;

private
private
private
private

String messageString = "Message";


Icon messageIcon = new ImageIcon("blue-ball.gif");
Object messageObject = new Date();
Component messageComponent = new SamplePanel();

}
/**
Un panneau avec un fond color
*/
class SamplePanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
Rectangle2D rect = new Rectangle2D.Double(0, 0,
getWidth() - 1, getHeight() - 1);
g2.setPaint(Color.YELLOW);
g2.fill(rect);
g2.setPaint(Color.BLUE);
g2.draw(rect);
}
public Dimension getMinimumSize()
{
return new Dimension(10, 10);
}
}
javax.swing.JOptionPane 1.2
static void showMessageDialog(Component parent,
int messageType, Icon icon)
static void showMessageDialog(Component parent,
int messageType)
static void showMessageDialog(Component parent,
static void showInternalMessageDialog(Component
title, int messageType, Icon icon)
static void showInternalMessageDialog(Component
title, int messageType)

Object message, String title,


Object message, String title,
Object message)
parent, Object message, String
parent, Object message, String

Livre Java .book Page 522 Jeudi, 25. novembre 2004 3:04 15

522

Au cur de Java 2 - Notions fondamentales

static void showInternalMessageDialog(Component parent, Object message)

Affichent une bote de dialogue de message ou une bote de dialogue de message interne (cest-dire entirement dessine au sein de son cadre conteneur).
Paramtres :

parent

Le composant parent (peut tre null).

message

Le message afficher dans la bote de dialogue (chane,


icne, composant ou tableau dobjets).

title

La chane dans la barre de titre de la bote de dialogue.

messageType

Lune des constantes suivantes : ERROR_MESSAGE,


INFORMATION_MESSAGE, WARNING_MESSAGE,
QUESTION_MESSAGE, PLAIN_MESSAGE.

icon

Licne afficher la place de lune des icnes standard.

static int showConfirmDialog(Component parent, Object message, String title,


int typeOption, int messageType, Icon icon)

static int showConfirmDialog(Component parent, Object message, String title,


int optionType, int messageType)

static int showConfirmDialog(Component parent, Object message, String title,


int optionType)

static int showConfirmDialog(Component parent, Object message)

static int showInternalConfirmDialog(Component parent, Object message, String


title, int optionType, int messageType)

static int showInternalConfirmDialog(Component parent, Object message, String


title, int optionType)

static int showInternalConfirmDialog(Component parent, Object message)

static int showInternalConfirmDialog(Component parent, Object message, String


title, int optionType, int messageType, Icon icon)

Affichent une bote de dialogue de confirmation ou une bote de dialogue de confirmation interne
(entirement dessine au sein de son cadre conteneur). Renvoient loption slectionne par
lutilisateur (parmi les suivantes : OK_OPTION, CANCEL_OPTION, YES_OPTION, NO_OPTION) ou
CLOSED_OPTION si lutilisateur a ferm la bote de dialogue.
Paramtres :

parent

Le composant parent (peut tre null).

message

Le message afficher dans la bote de dialogue (chane,


icne, composant ou tableau dobjets).

title

La chane dans la barre de titre de la bote de dialogue.

messageType

Lune des constantes suivantes : ERROR_MESSAGE,


INFORMATION_MESSAGE, WARNING_MESSAGE,
QUESTION_MESSAGE, PLAIN_MESSAGE.

optionType

Lune des constantes suivantes : DEFAULT_OPTION,


YES_NO_OPTION, YES_NO_CANCEL_OPTION,
OK_CANCEL_OPTION.

icon

Licne afficher la place dune des icnes standard.

Livre Java .book Page 523 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

523

static int showOptionDialog(Component parent, Object message, String title, int


optionType, int messageType, Icon icon, Object[] options, Object default)
static int showInternalOptionDialog(Component parent, Object message, String
title, int optionType, int messageType, Icon icon, Object[] options, Object
default)

Affichent une bote de dialogue doptions ou une bote de dialogue doptions interne (entirement dessine au sein de son cadre conteneur). Renvoient lindice de loption slectionne par
lutilisateur ou CLOSED_OPTION sil a ferm la bote de dialogue.
Paramtres :

parent

Le composant parent (peut tre null).

message

Le message afficher dans la bote de dialogue (chane,


icne, composant ou tableau dobjets).

title

La chane dans la barre de titre de la bote de dialogue.

messageType

Lune des constantes suivantes : ERROR_MESSAGE,


INFORMATION_MESSAGE, WARNING_MESSAGE,
QUESTION_MESSAGE, PLAIN_MESSAGE.

optionType

Lune des constantes suivantes : DEFAULT_OPTION,


YES_NO_OPTION, YES_NO_CANCEL_OPTION,
OK_CANCEL_OPTION.

icon

Licne afficher la place dune des icnes standard.

options

Un tableau doptions (chanes, icnes ou composants)

default

Loption par dfaut proposer lutilisateur.

static Object showInputDialog(Component parent, Object message, String title,


int messageType, Icon icon, Object[] values, Object default)
static String showInputDialog(Component parent, Object message, String title,
int messageType)
static String showInputDialog(Component parent, Object message)
static String showInputDialog(Object message)
static String showInputDialog(Component parent, Object message, Object default)
1.4
static String showInputDialog(Object message, Object default) 1.4
static Object showInternalInputDialog(Component parent, Object message, String
title, int messageType, Icon icon, Object[] values, Object default)
static String showInternalInputDialog(Component parent, Object message, String
title, int messageType)
static String showInternalInputDialog(Component parent, Object message)

Affichent une bote de dialogue de saisie ou une bote de dialogue de saisie interne. Renvoient la
chane tape par lutilisateur ou null sil a annul la bote de dialogue.
Paramtres :

parent

Le composant parent (peut tre null).

message

Le message afficher dans la bote de dialogue (chane,


icne, composant ou tableau dobjets).

title

La chane dans la barre de titre de la bote de dialogue.

messageType

Lune des constantes suivantes : ERROR_MESSAGE,


INFORMATION_MESSAGE, WARNING_MESSAGE,
QUESTION_MESSAGE, PLAIN_MESSAGE.

Livre Java .book Page 524 Jeudi, 25. novembre 2004 3:04 15

524

Au cur de Java 2 - Notions fondamentales

icon

Licne afficher la place dune des icnes standard.

values

Un tableau de valeurs afficher dans une zone de liste


droulante.

default

La valeur par dfaut proposer lutilisateur.

Cration de botes de dialogue


Dans la section prcdente, vous avez tudi lutilisation de la classe JOptionPane pour afficher une
bote de dialogue standard. Dans cette section, vous apprendrez crer vous-mme une bote de
dialogue.
La Figure 9.48 prsente un modle classique de bote de dialogue modale, une fentre dinformation
sur le programme en cours dexcution, qui est affiche lorsque lutilisateur clique sur loption
About.
Pour implmenter une bote de dialogue, vous devez driver une classe de JDialog. Ce processus est
identique celui qui permet de driver la fentre principale partir de la classe JFrame pour une
application. Voici les tapes suivre :
1. Dans le constructeur de la bote de dialogue, appelez le constructeur de la classe de base JDialog. Vous devez fournir le nom du cadre propritaire (la fentre de cadre dans laquelle saffiche
la bote de dialogue), le titre de la bote de dialogue et un indicateur boolen spcifiant si la bote
de dialogue doit tre modale ou non.
Vous devez indiquer le cadre propritaire afin que la bote de dialogue soit affiche par-dessus.
Les systmes de fentrage requirent gnralement que toute fentre affichable appartienne un
autre cadre. Vous pouvez aussi fournir un propritaire null, mais cest un peu risqu, la bote de
dialogue pourrait tre masque derrire dautres fentres (les botes de dialogue ayant un
propritaire null appartiennent en fait un cadre masqu et partag).
2. Ajoutez les composants dinterface de la bote de dialogue.
3. Ajoutez les gestionnaires dvnement.
4. Dfinissez la taille de la bote de dialogue.
Figure 9.48
Une bote de dialogue
About.

Voici un exemple :
public AboutDialog extends JDialog
{
public AboutDialog(JFrame owner)
{

Livre Java .book Page 525 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

525

super(owner, "About DialogTest ", true);


add(new JLabel(
"<html><h1><i>Core Java</i></h1><hr>
By Cay Horstmann and Gary Cornell</html>"),
BorderLayout.CENTER);
JPanel panel = new JPanel();
JButton ok = new JButton("OK");
ok.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setVisible(false);
}
});
panel.add(ok);
add(panel, BorderLayout.SOUTH);
setSize(250, 150);
}
}

Le constructeur ajoute les lments de linterface utilisateur, ici des tiquettes et un bouton. Il ajoute
aussi un gestionnaire au bouton et dfinit la taille de la bote de dialogue.
Pour afficher la bote de dialogue, vous crez un nouvel objet bote de dialogue et vous laffichez :
JDialog dialog = new AboutDialog(this);
dialog.setVisible(true);

En fait, dans le code ci-aprs, la bote de dialogue est cre une seule fois et peut ainsi tre rutilise
chaque fois que lutilisateur clique sur loption About :
if (dialog == null) // Premire fois
dialog = new AboutDialog(this);
dialog.setVisible(true);

Lorsque lutilisateur clique sur OK, la bote de dialogue doit se fermer. Cette action est gre par le
gestionnaire dvnement du bouton OK.
ok.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setVisible(false);
}
});

Si lutilisateur clique sur la case de fermeture (X) de la fentre, la bote de dialogue est galement
ferme. Comme pour la classe JFrame, vous pouvez redfinir ce comportement au moyen de la
mthode setDefaultCloseOperation.
LExemple 9.19 contient le code du programme de test de la bote de dialogue About.

Livre Java .book Page 526 Jeudi, 25. novembre 2004 3:04 15

526

Au cur de Java 2 - Notions fondamentales

Exemple 9.19 : DialogTest.java


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DialogTest
{
public static void main(String[] args)
{
DialogFrame frame = new DialogFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un menu dont laction Fichier->A propos (File->About)
affiche une bote de dialogue.
*/
class DialogFrame extends JFrame
{
public DialogFrame()
{
setTitle("DialogTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// construire un menu Fichier
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu fileMenu = new JMenu("File");
menuBar.add(fileMenu);
// ajouter les options A propos et Quitter au menu
// Loption A propos affiche la bote de dialogue correspondante
JMenuItem aboutItem = new JMenuItem("About");
aboutItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
if (dialog == null) // premire fois
dialog = new AboutDialog(DialogFrame.this);
dialog.setVisible(true); // bote de dialogue
}
});
fileMenu.add(aboutItem);
// Loption Quitter permet de quitter le programme
JMenuItem exitItem = new JMenuItem("Exit");

Livre Java .book Page 527 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

exitItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
fileMenu.add(exitItem);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
private AboutDialog dialog;
}
/**
Une bote de dialogue modale qui affiche un message et
attend que lutilisateur clique sur le bouton OK.
*/
class AboutDialog extends JDialog
{
public AboutDialog(JFrame owner)
{
super(owner, "About DialogTest", true);
// ajouter le libell HTML au centre
add(new JLabel(
"<html><h1><i>Core Java</i></h1><hr>

By Cay Horstmann and Gary Cornell</html>"),


BorderLayout.CENTER);
// Le bouton OK ferme la bote de dialogue
JButton ok = new JButton("OK");
ok.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setVisible(false);
}
});
// ajouter le bouton OK dans la partie sud
JPanel panel = new JPanel();
panel.add(ok);
add(panel, BorderLayout.SOUTH);
setSize(250, 150);
}
}

527

Livre Java .book Page 528 Jeudi, 25. novembre 2004 3:04 15

528

Au cur de Java 2 - Notions fondamentales

javax.swing.JDialog 1.2

public JDialog(Frame parent, String title, boolean modal)

Cre une bote de dialogue qui nest pas visible tant quelle na pas t explicitement affiche.
Paramtres :

parent

Le cadre propritaire de la bote de dialogue.

title

Le titre de la bote de dialogue.

modal

true pour les botes de dialogue modales (une bote


de dialogue modale empche linteraction avec les autres
fentres tant quelle nest pas ferme).

Echange de donnes
On conoit le plus souvent une bote de dialogue dans le but de recueillir des informations de la part
de lutilisateur. Vous avez pu constater que la cration dun objet bote de dialogue est une opration
simple : il suffit de fournir les donnes initiales, puis dappeler la mthode setVisible(true) pour
afficher la bote lcran. Nous allons maintenant tudier comment des donnes peuvent tre transfres dans une bote de dialogue, ou extraites.
Prenons lexemple de la bote de dialogue de la Figure 9.49, qui pourrait tre utilise pour obtenir un
nom et un mot de passe utilisateur pour la connexion un service en ligne.
Figure 9.49
Une bote de dialogue
de mot de passe.

Votre bote de dialogue doit fournir des mthodes pour dfinir les donnes par dfaut. Par exemple,
la classe PasswordChooser de notre exemple de programme possde une mthode setUser qui
insre les valeurs par dfaut dans les champs suivants :
public void setUser(User u)
{
username.setText(u.getName());
}

Une fois que vous avez, le cas chant, dfini les valeurs par dfaut, vous affichez la bote de dialogue en
appelant setVisible(true). La bote de dialogue saffiche.
Lutilisateur fournit les informations et clique sur le bouton OK ou Annuler. Les gestionnaires
dvnement pour les deux boutons appellent setVisible(false), qui termine lappel setVisible(true). Lutilisateur peut aussi fermer la bote de dialogue. Si vous navez pas install
dcouteur de fentre pour la bote de dialogue, le processus de fermeture de fentre par dfaut
sapplique : la bote de dialogue devient invisible, ce qui a aussi pour effet de mettre fin lappel
setVisible(true).

Livre Java .book Page 529 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

529

Ce dont il faut se souvenir, cest que lappel setVisible(true) se bloque jusqu ce que lutilisateur ait renvoy la bote de dialogue. Ceci facilite limplmentation des botes de dialogue modales.
Il vous faudra savoir si lutilisateur a accept ou annul la bote de dialogue. Notre exemple de code
dfinit la balise OK sur false avant dafficher la bote de dialogue. Seul le gestionnaire dvnement
du bouton OK dfinit la balise OK sur true. Dans ce cas, vous pouvez rcuprer la saisie de lutilisateur dans la bote de dialogue.
INFO
Le transfert de donnes partir dune bote de dialogue non modale nest pas une opration facile. Lors de laffichage dune telle bote de dialogue, lappel de la mthode show nest pas bloquant et le programme poursuit son
excution. Si lutilisateur slectionne une option puis clique sur OK, la bote de dialogue doit envoyer un vnement
un couteur dans le programme.

Notre programme contient une autre amlioration intressante. Lorsque vous construisez un objet
JDialog, vous devez en spcifier le cadre propritaire. Pourtant, assez souvent, vous voudrez afficher la mme bote de dialogue avec diffrents cadres propritaires. Il est plus facile de choisir le
cadre propritaire lorsque vous tes prt afficher la bote de dialogue, plutt que lorsque vous
construisez lobjet PasswordChooser.
Lastuce consiste faire driver PasswordChooser de JPanel au lieu de JDialog. Crez un objet
JDialog la vole dans la mthode showDialog:
public boolean showDialog(Frame owner, String title)
{
ok = false;
if (dialog == null || dialog.getOwner()!= owner)
{
dialog = new JDialog(owner, true);
dialog.add(this);
dialog.pack();
}
dialog.setTitle(title);
dialog.setVisible(true);
return ok;
}

Notez que owner peut valoir null.


Vous pouvez faire encore mieux. Il arrive que le cadre propritaire ne soit pas immdiatement disponible. Il est facile de le dterminer partir de nimporte quel composant parent, de la faon
suivante :
Frame owner;
if (parent instanceof Frame)
owner = (Frame) parent;
else
owner = (Frame) SwingUtilities.getAncestorOfClass(
Frame.class, parent);

Nous avons tir parti de cette amlioration dans notre programme. La classe JOptionPane utilise
aussi ce mcanisme.

Livre Java .book Page 530 Jeudi, 25. novembre 2004 3:04 15

530

Au cur de Java 2 - Notions fondamentales

De nombreuses botes de dialogue possdent un bouton par dfaut, automatiquement slectionn si


lutilisateur appuie sur une touche de dclenchement (Entre dans la plupart des implmentations
"look and feel"). Le bouton par dfaut est spcialement marqu, souvent par un soulign pais.
Vous dfinissez le bouton par dfaut dans le volet racine de la bote de dialogue :
dialog.getRootPane().setDefaultButton(okButton);

Si vous suivez notre suggestion de disposition de la bote de dialogue dans un panneau, vous devez
prendre garde ne dfinir le bouton par dfaut quaprs avoir envelopp le panneau dans une bote
de dialogue. Le panneau lui-mme na pas de volet racine.
LExemple 9.20 contient la totalit du code illustrant le flot des donnes entrant et sortant dune
bote de dialogue.
Exemple 9.20 : DataExchangeTest.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DataExchangeTest
{
public static void main(String[] args)
{
DataExchangeFrame frame = new DataExchangeFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un menu dont loption Fichier->Connecter (File->Connect)
affiche une bote de dialogue de mot de passe.
*/
class DataExchangeFrame extends JFrame
{
public DataExchangeFrame()
{
setTitle("DataExchangeTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// construire un menu Fichier
JMenuBar mbar = new JMenuBar();
setJMenuBar(mbar);
JMenu fileMenu = new JMenu("File");
mbar.add(fileMenu);
// ajouter les options Connecter et Quitter au menu
JMenuItem connectItem = new JMenuItem("Connect");
connectItem.addActionListener(new ConnectAction());
fileMenu.add(connectItem);
// Loption Quitter permet de quitter le programme

Livre Java .book Page 531 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

JMenuItem exitItem = new JMenuItem("Exit");


exitItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
fileMenu.add(exitItem);
textArea = new JTextArea();
add(new JScrollPane(textArea),
BorderLayout.CENTER);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
private PasswordChooser dialog = null;
private JTextArea textArea;
/**
Loption Connecter affiche la bote de dialogue mot de passe.
*/
private class ConnectAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
// Si premire fois, construire la bote de dialogue
if (dialog == null)
dialog = new PasswordChooser();
// dfinir les valeurs par dfaut
dialog.setUser(new User("yourname", null));
// afficher la bote de dialogue
if (dialog.showDialog(DataExchangeFrame.this,
"Connect"))
{
// si accept, extraire lentre de lutilisateur
User u = dialog.getUser();
textArea.append(
"user name= "+u.getName()
+", password = "+ (new String(u.getPassword()))
+"\n");
}
}
}
}

531

Livre Java .book Page 532 Jeudi, 25. novembre 2004 3:04 15

532

Au cur de Java 2 - Notions fondamentales

/**
Un slecteur de mot de passe affich dans une bote de dialogue
*/
class PasswordChooser extends JPanel
{
public PasswordChooser()
{
setLayout(new BorderLayout());
// construire un panneau avec les champs Nom dutilisateur
// et Mot de passe
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(2, 2));
panel.add(new JLabel("User name:"));
panel.add(username = new JTextField(""));
panel.add(new JLabel("Password:"));
panel.add(password = new JPasswordField(""));
add(panel, BorderLayout.CENTER);
// crer les boutons OK et Annuler pour
// fermer la bote de dialogue
okButton = new JButton("OK");
okButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
ok = true;
dialog.setVisible(false);
}
});
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
dialog.setVisible(false);
}
});
// ajouter les boutons dans la zone sud
JPanel buttonPanel = new JPanel();
buttonPanel.add(okButton);
buttonPanel.add(cancelButton);
add(buttonPanel, BorderLayout.SOUTH);
}
/**
Dfinit les valeurs par dfaut de la bote de dialogue.
@param u Les informations utilisateur par dfaut

Livre Java .book Page 533 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

*/
public void setUser(User u)
{
username.setText(u.getName());
}
/**
Rcupre les entres dans la bote de dialogue.
@return Un objet User dont ltat reprsente
les entres dans la bote de dialogue
*/
public User getUser()
{
return new User(username.getText(),
password.getPassword());
}
/**
Affiche le panneau slecteur dans une bote de dialogue
@param parent Un composant dans le cadre propritaire ou null
@param title Le titre de la bote de dialogue
*/
public boolean showDialog(Component parent, String title)
{
ok = false;
// localiser le cadre propritaire
Frame owner = null;
if (parent instanceof Frame)
owner = (Frame) parent;
else
owner = (Frame)SwingUtilities.getAncestorOfClass(
Frame.class, parent);
// si premire fois, ou si le propritaire a chang,
// crer une nouvelle bote de dialogue
if (dialog == null || dialog.getOwner()!= owner)
{
dialog = new JDialog(owner, true);
dialog.add(this);
dialog.getRootPane().setDefaultButton(okButton);
dialog.pack();
}
// dfinir le titre et afficher la bote de dialogue
dialog.setTitle(title);
dialog.setVisible(true);
return ok;
}
private
private
private
private
private
}

JTextField username;
JPasswordField password;
JButton okButton;
boolean ok;
JDialog dialog;

533

Livre Java .book Page 534 Jeudi, 25. novembre 2004 3:04 15

534

Au cur de Java 2 - Notions fondamentales

/**
Un utilisateur a un nom et un mot de passe. Pour des raisons
de scurit, le mot de passe est stock sous forme de
tableau de caractres (char[]), au lieu dune chane.
*/
class User
{
public User(String aName, char[] aPassword)
{
name = aName;
password = aPassword;
}
public String getName() { return name; }
public char[] getPassword() { return password; }
public void setName(String aName) { name = aName; }
public void setPassword(char[] aPassword)
{ password = aPassword; }
private String name;
private char[] password;
}
javax.swing.SwingUtilities 1.2

Container getAncestorOfClass(Class c, Component comp)

Renvoie le conteneur parent le plus interne du composant donn qui appartient la classe donne
ou lune de ses sous-classes.
javax.swing.JComponent 1.2

JRootPane getRootPane()

Rcupre le volet racine entourant ce composant ou null si ce composant ne possde pas


danctre avec un volet racine.
javax.swing.JRootPane 1.2

void setDefaultButton(JButton button)

Dfinit le bouton par dfaut pour ce volet racine. Pour dsactiver le bouton par dfaut, appelez
cette mthode avec un paramtre null.
javax.swing.JButton 1.2

boolean isDefaultButton()

Renvoie true si ce bouton est le bouton par dfaut de son volet racine.

Botes de dialogue Fichier


Lorsque vous crivez une application, il est souvent ncessaire douvrir et denregistrer des fichiers.
Une bote de dialogue Fichier (File) bien conue, qui affiche les fichiers et les rpertoires et permet
lutilisateur de naviguer dans le systme de fichiers, est difficile dvelopper. Il est de plus totalement inutile de vouloir "rinventer la roue", puisque Swing propose une classe JFileChooser qui
permet dafficher une bote de dialogue Fichier semblable celle quutilisent la plupart des applications natives. Les botes de dialogue JFileChooser sont toujours modales. Notez que cette classe

Livre Java .book Page 535 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

535

nest pas une sous-classe de JDialog. Au lieu dappeler setVisible(true), vous appelez la
mthode showOpenDialog pour afficher une bote de dialogue douverture de fichier ou showSaveDialog pour afficher une bote de dialogue denregistrement de fichier. Le bouton utilis pour accepter un fichier est automatiquement libell Open (Ouvrir) ou Save (Enregistrer). Vous pouvez
galement spcifier votre propre intitul de bouton laide de la mthode showDialog. La
Figure 9.50 montre un exemple de bote de dialogue de slection de fichier.
Figure 9.50
Une bote de dialogue
de slection de fichier.

Voici les tapes suivre pour mettre en uvre une bote de dialogue Fichier et rcuprer la slection
de lutilisateur :
1. Crez un objet JFileChooser. A la diffrence du constructeur de la classe JDialog, vous navez
pas besoin dindiquer le composant parent, ce qui vous permet de rutiliser une mme bote de
dialogue avec plusieurs cadres.
Par exemple :
JFileChooser chooser = new JFileChooser();

ASTUCE
Rutiliser un objet slecteur de fichier est une bonne ide, car le constructeur de JFileChooser peut se rvler
assez lent, surtout sous Windows, si lutilisateur a de nombreux mappages dunits rseau.

2. Dfinissez le rpertoire de travail en appelant la mthode setCurrentDirectory.


Utilisez le rpertoire de travail courant, par exemple
chooser.setCurrentDirectory(new File("."));

pour travailler avec le rpertoire courant. Vous devez fournir un objet File (voir Chapitre 12).
Tout ce quil vous faut savoir est quil existe un constructeur File (String nomfichier), qui
transforme un nom de fichier ou de rpertoire en un objet File.
3. Si lutilisateur peut choisir un nom de fichier par dfaut, appelez la mthode setSelectedFile
de la faon suivante :
chooser.setSelectedFile(new File( nomFichier));

Livre Java .book Page 536 Jeudi, 25. novembre 2004 3:04 15

536

Au cur de Java 2 - Notions fondamentales

4. Pour permettre lutilisateur de slectionner plusieurs fichiers, appelez la mthode setMultiSelectionEnabled. Son utilisation est peu courante, et bien sr optionnelle :
chooser.setMultiSelectionEnabled(true);

5. Si vous voulez limiter laffichage un type de fichier particulier (par exemple tous les fichiers
avec lextension .gif), vous devez alors dfinir un filtre de fichier, dont nous reparlerons plus loin
dans cette section.
6. Par dfaut, un utilisateur ne peut slectionner que des fichiers dans un slecteur de fichier. Si
vous voulez quil soit capable de slectionner des rpertoires, utilisez la mthode setFileSelectionMode, de la faon suivante :
JFileChooser.FILES_ONLY (fichiers seulement, par dfaut),
JFileChooser.DIRECTORIES_ONLY (rpertoires seulement) ou
JFileChooser.FILES_AND_DIRECTORIES (les deux).
7. Affichez la bote de dialogue en appelant la mthode showOpenDialog ou showSaveDialog.
Vous devez indiquer le composant parent pour ces appels :
int result = chooser.showOpenDialog(parent);

ou
int result = chooser.showSaveDialog(parent);

La seule diffrence entre ces appels est le libell du "bouton dapprobation", celui sur lequel
lutilisateur clique pour mettre fin la slection de fichier. Vous pouvez aussi appeler la mthode
showDialog et passer un texte explicite pour le bouton dapprobation :
int result = chooser.showDialog(parent, "Select");

A la suite de ces appels, le flux dexcution du programme ne revient pas tant que lutilisateur na pas accept un fichier ou annul son action. La valeur renvoye est JFileChooser.APPROVE_OPTION, JFileChooser.CANCEL_OPTION ou JFileChooser.ERROR_OPTION.
8. Vous rcuprez le ou les fichiers slectionns au moyen de la mthode getSelectedFile() ou
getSelectedFiles(). Ces mthodes renvoient soit un seul objet File, soit un tableau dobjets
File. Si vous avez uniquement besoin du nom de lobjet fichier, appelez sa mthode getPath.
Par exemple :
String filename = chooser.getSelectedFile().getPath();

Ces tapes sont dans lensemble assez simples. La principale difficult de limplmentation
dune bote de dialogue Fichier rside dans la spcification dun sous-ensemble de fichiers
partir duquel lutilisateur pourra choisir. Supposons quil ait choisir un fichier dimages GIF.
Le slecteur de fichier ne devrait alors afficher que les fichiers dots de lextension .gif. Il
faudrait aussi quil puisse signifier lutilisateur que les fichiers affichs sont dune certaine
catgorie, comme "Images GIF". Mais la situation est parfois plus complexe. Si lutilisateur doit
choisir un fichier dimages JPEG, lextension peut tre soit .jpg, soit .jpeg. Plutt que dlaborer un mcanisme permettant de codifier ces complexits, les concepteurs fournissent un mcanisme plus lgant : pour limiter les fichiers affichs, vous fournissez un objet qui tend la classe

Livre Java .book Page 537 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

537

abstraite javax.swing.filechooser.FileFilter. Le slecteur de fichier passe chaque fichier


au filtre et affiche seulement ceux quil a accepts.
Au moment o ce livre a t crit, une seule sous-classe de ce type tait fournie, le filtre par dfaut
qui accepte tous les fichiers.
Vous pouvez toutefois facilement crire vos propres filtres. Il suffit dimplmenter les deux mthodes
abstraites de la superclasse FileFilter:
public boolean accept(File f);
public String getDescription();

INFO
Il existe dans le package java.io une interface FileFilter, sans aucun rapport, et qui possde une seule
mthode, boolean accept(File f). Elle est utilise dans la mthode listFiles de la classe File pour numrer les fichiers dun rpertoire. On peut se demander pourquoi les concepteurs de Swing nont pas tendu cette interface peut-tre que la bibliothque de classes Java est devenue maintenant si complexe que mme les
programmeurs de Sun ne connaissent pas toutes les classes et interfaces standard.
Vous devrez rsoudre le conflit de nom entre ces deux types homonymes, si vous importez la fois les packages

java.io et javax.swing.filechooser. Le remde le plus simple consiste importer javax.swing.filechooser.FileFilter, et non javax.swing.filechooser.*.

La premire mthode vrifie si un fichier doit tre accept. La seconde renvoie une description du
type de fichier qui peut tre affich dans la bote de dialogue. Par exemple, pour filtrer les fichiers
GIF, procdez de la faon suivante :
public class GifFilter extends FileFilter
{
public boolean accept(File f)
{
return f.getName().toLowerCase().endsWith(".gif")
|| f.isDirectory();
}
public String getDescription()
{
return "GIF Image";
}
}

Une fois que vous disposez dun objet filtre de fichier, utilisez la mthode setFileFilter de la
classe JFileChooser pour limplmenter dans lobjet slecteur de fichier :
chooser.setFileFilter(new GifFilter());

Dans notre exemple de programme, nous avons fourni une classe ExtensionFileFilter, utiliser
de la faon suivante :
ExtensionFileFilter filter = new ExtensionFileFilter();
filter.addExtension("jpg");
filter.addExtension("gif");
filter.setDescription("Image files");

Livre Java .book Page 538 Jeudi, 25. novembre 2004 3:04 15

538

Au cur de Java 2 - Notions fondamentales

Limplmentation de la classe ExtensionFileFilter est une simple gnralisation de la classe


GifFilter. Vous pouvez utiliser cette classe dans vos propres programmes.
INFO
Le JDK contient une classe ExampleFileFilter similaire dans le rpertoire demo/jfc/FileChooserDemo.

Vous pouvez installer plusieurs filtres dans le slecteur de fichier en appelant


chooser.addChoosableFileFilter(new GifFilter());
chooser.addChoosableFileFilter(new JpegFilter());
. . .

Lutilisateur slectionne un filtre dans la zone de liste droulante situe en bas de la bote de dialogue. Par dfaut, le filtre "All files" (Tous fichiers) est toujours prsent dans la liste. Il est recommand
de procder ainsi pour toutes les botes de dialogue Fichier, pour le cas o un utilisateur de votre
programme aurait besoin de slectionner un fichier dont lextension nest pas standard. Si toutefois
vous vouliez supprimer cette option "Tous fichiers", appelez
chooser.setAcceptAllFileFilterUsed(false)

ATTENTION
Si vous rutilisez un slecteur de fichier unique pour charger et enregistrer diffrents types de fichiers, appelez
chooser.resetChoosableFilters()

pour effacer tout filtre de fichier ancien avant den ajouter de nouveaux.

Enfin, vous pouvez personnaliser la bote de dialogue de slection de fichier en y ajoutant des icnes
spciales et une description pour chaque fichier affich. Pour cela, vous devez fournir un objet
dune classe qui tend la classe FileView du package javax.swing.filechooser. Il sagit l dune
technique avance. Normalement, vous navez pas fournir une vue de fichier, car le style dinterface dynamique implment (LookAndFeel) sen charge votre place. Mais si vous souhaitez afficher diffrentes icnes pour des types de fichiers particuliers, vous pouvez implmenter votre propre
vue de fichier. Vous devez tendre la classe FileView et implmenter cinq mthodes :
Icon getIcon(File f);
String getName(File f);
String getDescription(File f);
String getTypeDescription(File f);
Boolean isTraversable(File f);

Vous appelez ensuite la mthode setFileView pour implmenter votre vue de fichier dans le slecteur
de fichier.
Le slecteur appelle vos mthodes pour chaque fichier ou rpertoire quil souhaite afficher. Si une
mthode relative licne, au nom ou la description renvoie null, le slecteur consulte alors la vue
de fichier par dfaut du style dinterface implment. Tout cela signifie que vous ne devez grer que
les types de fichiers auxquels vous rservez un traitement diffrent.
Le slecteur de fichier appelle la mthode isTraversable pour dcider de louverture dun rpertoire lorsquun utilisateur clique dessus. Notez que cette mthode renvoie un objet Boolean et non

Livre Java .book Page 539 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

539

pas une valeur boolean! Lopration parat trange, elle est pourtant approprie. Si vous voulez
conserver la vue de fichier par dfaut, contentez-vous de renvoyer null. Le slecteur de fichier
consultera alors la vue par dfaut. En dautres termes, la mthode renvoie un objet Boolean pour
vous donner le choix entre trois options : vrai (Boolean.TRUE), faux (Boolean.FALSE) ou null si
cela na pas dimportance.
Lexemple de programme contient une simple classe vue de fichier. Elle affiche une icne particulire chaque fois quun fichier correspond un filtre. Nous lutilisons pour afficher une icne
palette pour tous les fichiers images :
class FileIconView extends FileView
{
public FileIconView(FileFilter aFilter, Icon anIcon)
{
filter = aFilter;
icon = anIcon;
}
public Icon getIcon(File f)
{
if (!f.isDirectory() && filter.accept(f))
return icon;
else return null;
}
private FileFilter filter;
private Icon icon;
}

ATTENTION
Dans la version 1.2 du JDK, vous devez dfinir les cinq mthodes de votre sous-classe FileView. Renvoyez simplement null pour les mthodes dont vous navez pas besoin. Dans la version 1.3 du JDK, les mthodes de FileView
ne sont plus abstraites dsormais.

Vous installez cette vue de fichier dans votre slecteur de fichier laide de la mthode setFileView:
chooser.setFileView(new FileIconView(filter,
new ImageIcon("palette.gif")));

Le slecteur de fichier affichera alors licne "palette" en regard des fichiers Java qui passent le filtre
et utilisera la vue de fichier par dfaut pour tous les autres fichiers. Nous utilisons bien entendu le
mme filtre que celui dfini dans le slecteur de fichier.
ASTUCE
Vous trouverez dans le rpertoire demo/jfc/FileChooserDemo du JDK, la classe ExampleFileView qui permet
dassocier des icnes et des descriptions des extensions arbitraires.

Livre Java .book Page 540 Jeudi, 25. novembre 2004 3:04 15

540

Au cur de Java 2 - Notions fondamentales

Enfin, vous pouvez personnaliser une bote de dialogue Fichier en ajoutant un composant accessoire.
Par exemple, la Figure 9.51 montre un accessoire daperu en regard de la liste de fichiers. Cet
accessoire affiche une vue miniature du fichier actuellement slectionn.
Figure 9.51
Une bote de dialogue
Fichier avec un accessoire
daperu.

Un accessoire peut tre nimporte quel composant Swing. Dans notre exemple, nous tendons la
classe JLabel et affectons son icne une copie rduite de limage graphique :
class ImagePreviewer extends JLabel
{
public ImagePreviewer(JFileChooser chooser)
{
setPreferredSize(new Dimension(100, 100));
setBorder(BorderFactory.createEtchedBorder());
}
public void loadImage(File f)
{
ImageIcon icon = new ImageIcon(f.getPath());
if(icon.getIconWidth() > getWidth())
icon = new ImageIcon(icon.getImage().getScaledInstance(
getWidth(), -1, Image.SCALE_DEFAULT));
setIcon(icon);
repaint();
}
}

Il reste un dfi relever. Nous voulons mettre jour limage daperu chaque fois que lutilisateur
slectionne un fichier diffrent. Le slecteur de fichier utilise le mcanisme "Java Beans" de notification aux couteurs intresss chaque fois quune de ses proprits change. Le fichier slectionn est
une proprit que vous pouvez surveiller en installant un PropertyChangeListener. Nous reviendrons sur ce mcanisme plus en dtail dans Au cur de Java 2 Volume 2, ditions CampusPress,
2003. Voici le code ncessaire pour intercepter les notifications :
chooser.addPropertyChangeListener(new
PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent event)
{

Livre Java .book Page 541 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

541

if (event.getPropertyName() ==
JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)
{
File newFile = (File) event.getNewValue()
// mettre jour laccessoire
...
}
}
});

Dans notre exemple de programme, nous ajoutons ce code au constructeur de ImagePreviewer.


LExemple 9.21 contient une adaptation du programme ImageViewer du Chapitre 2, dans laquelle le
slecteur de fichier a t amlior avec une vue personnalise du fichier et un accessoire daperu.
Exemple 9.21 : FileChooserTest.java
import
import
import
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.awt.image.*;
java.beans.*;
java.util.*;
java.io.*;
javax.swing.*;
javax.swing.filechooser.FileFilter;
javax.swing.filechooser.FileView;

public class FileChooserTest


{
public static void main(String[] args)
{
ImageViewerFrame frame = new ImageViewerFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un menu pour le chargement dune image et
une zone daffichage pour limage charge.
*/
class ImageViewerFrame extends JFrame
{
public ImageViewerFrame()
{
setTitle("FileChooserTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// configurer la barre de menus
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem openItem = new JMenuItem("Open");
menu.add(openItem);
openItem.addActionListener(new FileOpenListener());
JMenuItem exitItem = new JMenuItem("Exit");

Livre Java .book Page 542 Jeudi, 25. novembre 2004 3:04 15

542

Au cur de Java 2 - Notions fondamentales

menu.add(exitItem);
exitItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
// utiliser un label pour afficher les images
label = new JLabel();
add(label);
// configurer le slecteur de fichier
chooser = new JFileChooser();
// accepter tous les fichiers images avec
// lextension .jpg, .jpeg, .gif
final ExtensionFileFilter filter
= new ExtensionFileFilter();
filter.addExtension("jpg");
filter.addExtension("jpeg");
filter.addExtension("gif");
filter.setDescription("Image files");
chooser.setFileFilter(filter);
chooser.setAccessory(new ImagePreviewer(chooser));
chooser.setFileView(new FileIconView(filter,
new ImageIcon("palette.gif")));
}
/**
Ecouteur du menu File->Open.
*/
private class FileOpenListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
chooser.setCurrentDirectory(new File("."));
// afficher la bote de dialogue de slection de fichier
int result
= chooser.showOpenDialog(ImageViewerFrame.this);
// si le fichier image est accept, le dfinir comme
// icne pour le label
if(result == JFileChooser.APPROVE_OPTION)
{
String name
= chooser.getSelectedFile().getPath();
label.setIcon(new ImageIcon(name));
}
}
}

Livre Java .book Page 543 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

public static final int DEFAULT_WIDTH = 300;


public static final int DEFAULT_HEIGHT = 400;
private JLabel label;
private JFileChooser chooser;
}
/**
Ce filtre recherche tous les fichiers ayant
un jeu dextensions donn.
*/
class ExtensionFileFilter extends FileFilter
{
/**
Ajoute une extension reconnue par ce filtre de fichier.
@param extension Une extension fichier (telle que ".txt" ou "txt")
*/
public void addExtension(String extension)
{
if (!extension.startsWith("."))
extension = "."+extension;
extensions.add(extension.toLowerCase());
}
/**
Dfinit une description pour le jeu de fichiers
que ce filtre reconnat.
@param aDescription Une description pour le jeu de fichiers
*/
public void setDescription(String aDescription)
{
description = aDescription;
}
/**
Renvoie une description pour le jeu de fichiers que
ce filtre reconnat.
@return Une description pour le jeu de fichiers
*/
public String getDescription()
{
return description;
}
public boolean accept(File f)
{
if (f.isDirectory()) return true;
String name = f.getName().toLowerCase();
// vrifier si le nom du fichier se termine par
// lune quelconque des extensions
for (String extension: extensions)
if (name.endsWith(extension))
return true;
return false;
}

543

Livre Java .book Page 544 Jeudi, 25. novembre 2004 3:04 15

544

Au cur de Java 2 - Notions fondamentales

private String description = "";


private ArrayList<String> extensions = new ArrayList<String>();
}
/**
Une vue du fichier qui affiche une icne pour tous les
fichiers qui correspondent un filtre
*/
class FileIconView extends FileView
{
/**
Construit un FileIconView.
@param aFilter Un filtre de fichier - tous les fichiers que
ce filtre accepte safficheront avec licne
@param anIcon Licne affiche avec les fichiers reconnus.
*/
public FileIconView(FileFilter aFilter, Icon anIcon)
{
filter = aFilter;
icon = anIcon;
}
public Icon getIcon(File f)
{
if (!f.isDirectory() && filter.accept(f))
return icon;
else return null;
}
private FileFilter filter;
private Icon icon;
}
/**
Un accessoire de slecteur de fichier donnant un
aperu des images.
*/
class ImagePreviewer extends JLabel
{
/**
Construit un ImagePreviewer.
@param chooser Le slecteur de fichier dont la proprit change,
dclenche un changement de limage dans laperu
*/
public ImagePreviewer(JFileChooser chooser)
{
setPreferredSize(new Dimension(100, 100));
setBorder(BorderFactory.createEtchedBorder());
chooser.addPropertyChangeListener(new
PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent
event)
{
if (event.getPropertyName() ==
JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)

Livre Java .book Page 545 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

545

{
// lutilisateur a slectionn un nouveau fichier
File f = (File)event.getNewValue();
if (f == null) { setIcon(null); return; }
// rcuprer limage sous forme dicne
ImageIcon icon = new ImageIcon(f.getPath());
// si licne est trop grande, la rduire
if(icon.getIconWidth() > getWidth())
icon = new ImageIcon(
icon.getImage().getScaledInstance(
getWidth(), -1, Image.SCALE_DEFAULT));
setIcon(icon);
}
}
});
}
}
javax.swing.JFileChooser 1.2

JFileChooser()

Cre une bote de dialogue de slection de fichier qui peut tre employe pour plusieurs cadres.

void setCurrentDirectory(File dir)

Dfinit le rpertoire initial de la bote de dialogue Fichier.

void setSelectedFile(File file)


void setSelectedFiles(File[] file)

Dfinissent le choix de fichier par dfaut de la bote de dialogue Fichier.

void setMultiSelectionEnabled(boolean b)

Active ou dsactive le mode de slection multiple.

void setFileSelectionMode(intmode)

Permet lutilisateur de slectionner des fichiers seulement (par dfaut), des rpertoires seulement ou les deux. Le paramtre mode est lune des constantes JFileChooser.FILES_ONLY,
JFileChooser.DIRECTORIES_ONLY et JFileChooser.FILES_AND_DIRECTORIES.

int showOpenDialog(Component parent)


int showSaveDialog(Component parent)
int showDialog(Component parent, String approveButtonText)

Affichent une bote de dialogue dans laquelle le bouton dapprobation est libell "Open," "Save,"
ou avec la chane approveButtonText. Renvoient APPROVE_OPTION, CANCEL_OPTION (si lutilisateur a choisi le bouton Annuler ou renvoy la bote de dialogue) ou ERROR_OPTION (en cas
derreur).

File getSelectedFile()
File[] getSelectedFiles()

Rcuprent le ou les fichiers slectionns par lutilisateur (ou renvoient null sil na slectionn
aucun fichier).

Livre Java .book Page 546 Jeudi, 25. novembre 2004 3:04 15

546

Au cur de Java 2 - Notions fondamentales

void setFileFilter(FileFilter filter)

Dfinit le filtre de fichier pour la bote de dialogue. Tous les fichiers pour lesquels
filter.accept renvoie true seront affichs. Ajoute galement le filtre la liste des filtres slectionnables.

void addChoosableFileFilter(FileFilter filter)

Ajoute un filtre de fichier la liste des filtres slectionnables.

void setAcceptAllFileFilterUsed(boolean b)

Inclut ou supprime un filtre sur tous les fichiers dans la liste droulante des filtres.

void resetChoosableFileFilters()

Efface la liste des filtres slectionnables. Seul le filtre de tous les fichiers demeure, moins quil
ne soit explicitement supprim.

void setFileView(FileView view)

Dfinit une vue de fichier pour fournir des informations sur les fichiers affichs par le slecteur
de fichier.

void setAccessory(JComponent component)

Dfinit un composant accessoire.


javax.swing.filechooser.FileFilter 1.2

boolean accept(File f)

Renvoie true si le slecteur de fichier doit afficher le fichier.

String getDescription()

Renvoie une description du filtre de fichier, par exemple "Fichiers images (*.gif,*.jpeg)".
javax.swing.filechooser.FileView 1.2

String getName(File f)

Renvoie le nom du fichier f ou null. Normalement, cette mthode renvoie simplement


f.getName().

String getDescription(File f)

Renvoie une description lisible du fichier f ou null. Par exemple, si f est un document HTML,
cette mthode peut renvoyer son titre.

String getTypeDescription(File f)

Renvoie une description lisible du fichier f ou null. Par exemple, si f est un document HTML,
cette mthode peut renvoyer une chane "Document hypertexte".

Icon getIcon(File f)

Renvoie une icne pour le fichier f ou null. Par exemple, si f est un fichier JPEG, cette mthode
peut renvoyer une icne.

Boolean isTraversable(File f)

Renvoie Boolean.TRUE si f est un rpertoire que lutilisateur peut ouvrir. Cette mthode peut
renvoyer false si le rpertoire est, dun point de vue conceptuel, un document compos. Comme
toutes les mthodes FileView, celle-ci peut renvoyer null pour indiquer que le slecteur de fichier
doit adopter la vue de fichier par dfaut.

Livre Java .book Page 547 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

547

Slecteurs de couleur
Comme vous lavez vu dans la section prcdente, un slecteur de fichier performant est un composant dinterface utilisateur complexe, et il est hors de question que vous limplmentiez vous-mme.
De nombreuses botes outils dinterface utilisateur fournissent dautres botes de dialogue communes pour slectionner une date/heure, une valeur montaire, une police, une couleur, etc., que fournissent de nombreux outils dinterface utilisateur. Lintrt est double. Les programmeurs peuvent
simplement utiliser une implmentation de qualit, plutt que de crer la leur propre. Et les utilisateurs
ont une exprience intuitive de ces outils de slection.
Actuellement, Swing ne fournit quun seul slecteur supplmentaire, JColorChooser (voir Figures 9.52 9.54). Il permet aux utilisateurs de choisir une valeur de couleur. Tout comme la classe
JFileChooser, le slecteur de couleur est un composant, et non une bote de dialogue, mais il
contient des mthodes pour crer des botes de dialogue contenant un composant pour la slection
dune couleur.
Voici comment afficher une bote de dialogue modale avec un slecteur de couleur :
Color selectedColor = JColorChooser.showDialog( parent,
titre, couleurInitiale);

Vous pouvez aussi afficher une bote de dialogue slecteur de couleur non modale. Vous devez
fournir :
m

un composant parent ;

le titre de la bote de dialogue ;

un indicateur pour slectionner une bote de dialogue, soit modale, soit non modale ;

un slecteur de couleur ;

des couteurs pour les boutons OK et Annuler (ou null si vous ne voulez pas dcouteur).

Figure 9.52
Le panneau
"chantillons" dun
slecteur de couleur.

Livre Java .book Page 548 Jeudi, 25. novembre 2004 3:04 15

548

Au cur de Java 2 - Notions fondamentales

Figure 9.53
Le panneau HSB
(teinte-saturation-luminance)
dun slecteur de couleur.

Figure 9.54
Le panneau RGB
(rouge-vert-bleu)
dun slecteur de couleur.

Voici comment crer une bote de dialogue non modale qui dfinit la couleur darrire-plan lorsque
lutilisateur clique sur le bouton OK :
chooser = new JColorChooser();
dialog = JColorChooser.createDialog(
parent,
"Background color",
false /* non modale */,
chooser,
new ActionListener() // couteur du bouton OK

Livre Java .book Page 549 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

549

{
public void actionPerformed(ActionEvent event)
{
setBackground(chooser.getColor());
}
},
null /* pas dcouteur pour le bouton Annuler */);

Vous pouvez faire encore mieux, et renvoyer immdiatement lutilisateur des informations sur la
slection de couleur. Pour surveiller les slections de couleur, vous devez rcuprer le modle de
slection du slecteur et ajouter un couteur de changement :
chooser.getSelectionModel().addChangeListener(new
ChangeListener()
{
public void stateChanged(ChangeEvent event)
{
faire quelque chose avec chooser.getColor();
}
});

Dans ce cas, les boutons OK et Cancel fournis par la bote de dialogue de slection de couleur nont
aucun intrt. Vous pouvez simplement ajouter le composant slecteur de couleur dans une bote de
dialogue non modale :
dialog = new JDialog(parent, false /* non modale */);
dialog.add(chooser);
dialog.pack();

Le programme de lExemple 9.22 montre les trois types de botes de dialogue. Si vous cliquez sur le
bouton Modal, vous devrez slectionner une couleur avant de faire quoi que ce soit dautre. Si vous
cliquez sur le bouton Modeless, vous obtenez une bote de dialogue non modale, mais le changement
de couleur ne se produira que lorsque vous cliquez sur le bouton OK de la bote de dialogue. Si vous
choisissez le bouton Immediate, vous obtenez une bote de dialogue non modale sans boutons, et ds
que vous slectionnerez une couleur diffrente, larrire-plan du panneau sera mis jour.
Ici se termine notre tude des composants dinterface utilisateur. Les informations des Chapitres 7
9 vous ont appris comment implmenter des interfaces utilisateur graphiques simples avec Swing.
Reportez-vous au Volume 2 en ce qui concerne les composants Swing avancs et les techniques
graphiques sophistiques.
Exemple 9.22 : ColorChooserTest.java
import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;

public class ColorChooserTest


{
public static void main(String[] args)
{
ColorChooserFrame frame = new ColorChooserFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}

Livre Java .book Page 550 Jeudi, 25. novembre 2004 3:04 15

550

Au cur de Java 2 - Notions fondamentales

/**
Un cadre avec un panneau slecteur de couleur
*/
class ColorChooserFrame extends JFrame
{
public ColorChooserFrame()
{
setTitle("ColorChooserTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter le panneau slecteur de couleur au cadre
ColorChooserPanel panel = new ColorChooserPanel();
add(panel);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
/**
Un panneau avec des boutons pour afficher
trois types de slecteurs de couleur
*/
class ColorChooserPanel extends JPanel
{
public ColorChooserPanel()
{
JButton modalButton = new JButton("Modal");
modalButton.addActionListener(new ModalListener());
add(modalButton);
JButton modelessButton = new JButton("Modeless");
modelessButton.addActionListener(new ModelessListener());
add(modelessButton);
JButton immediateButton = new JButton("Immediate");
immediateButton.addActionListener(new ImmediateListener());
add(immediateButton);
}
/**
Cet couteur affiche un slecteur de couleur modal
*/
private class ModalListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Color defaultColor = getBackground();
Color selected = JColorChooser.showDialog(
ColorChooserPanel.this,
"Set background",
defaultColor);
if (selected!= null) setBackground(selected);
}
}
/**
Cet couteur affiche un slecteur de couleur non modal.

Livre Java .book Page 551 Jeudi, 25. novembre 2004 3:04 15

Chapitre 9

Swing et les composants dinterface utilisateur

La couleur du panneau est change lorsque lutilisateur


clique sur le bouton OK.
*/
private class ModelessListener implements ActionListener
{
public ModelessListener()
{
chooser = new JColorChooser();
dialog = JColorChooser.createDialog(
ColorChooserPanel.this,
"Background color",
false /* non modal */,
chooser,
new ActionListener() // couteur bouton OK
{
public void actionPerformed(ActionEvent event)
{
setBackground(chooser.getColor());
}
},
null /* pas dcouteur pour le bouton Annuler */);
}
public void actionPerformed(ActionEvent event)
{
chooser.setColor(getBackground());
dialog.setVisible(true);
}
private JDialog dialog;
private JColorChooser chooser;
}
/**
Cet couteur affiche un slecteur de couleur non modal.
La couleur du panneau change ds que lutilisateur
slectionne une nouvelle couleur.
*/
private class ImmediateListener implements ActionListener
{
public ImmediateListener()
{
chooser = new JColorChooser();
chooser.getSelectionModel().addChangeListener(new
ChangeListener()
{
public void stateChanged(ChangeEvent event)
{
setBackground(chooser.getColor());
}
});
dialog = new JDialog(
(Frame)null,
false /* non modal */);
dialog.add(chooser);
dialog.pack();
}

551

Livre Java .book Page 552 Jeudi, 25. novembre 2004 3:04 15

552

Au cur de Java 2 - Notions fondamentales

public void actionPerformed(ActionEvent event)


{
chooser.setColor(getBackground());
dialog.setVisible(true);
}
private JDialog dialog;
private JColorChooser chooser;
}
}
java.swing.JColorChooser 1.2
JColorChooser()

Construit un slecteur de couleur avec le blanc comme couleur initiale.

Color getColor()
void setColor(Color c)

Permettent dobtenir et de dfinir la couleur actuelle de ce slecteur de couleur.

static Color showDialog(Component parent, String title, Color initialColor)

Affiche une bote de dialogue modale qui contient un slecteur de couleur.


Paramtres :

parent

Le composant sur lequel doit apparatre la bote de dialogue.

title

Le titre de la bote de dialogue.

initialColor

La couleur initiale afficher dans le slecteur de couleur.

static JDialog createDialog(Component parent, String title, boolean modal, JColo rChooser chooser, ActionListener okListener, ActionListener cancelListener)

Cre une bote de dialogue qui contient un slecteur de couleur.


Paramtres :

parent

Le composant o doit apparatre la bote de dialogue.

title

Le titre de la bote de dialogue.

modal

true si cet appel doit empcher toute autre action jusqu la


fermeture de la bote de dialogue.

chooser

Le slecteur de couleur ajouter la bote de dialogue.

okListener
cancelListener Les couteurs des boutons OK et Cancel.

Livre Java .book Page 553 Jeudi, 25. novembre 2004 3:04 15

10
Dployer des applets
et des applications
Au sommaire de ce chapitre

Introduction aux applets


Balises HTML et attributs pour applets
Le multimdia
Le contexte dapplet
Les fichiers JAR
Packaging des applications
Java Web Start
Stockage des prfrences des applications
En principe, vous utilisez maintenant sans difficult la quasi-totalit des caractristiques du
langage Java. De plus, vous avez bnfici dune solide introduction aux bases de la programmation graphique en Java. Vous conviendrez que Java est un langage universel de programmation
orient objet, agrable mme sil nest pas parfait et que les bibliothques de linterface
utilisateur de Swing sont souples et efficaces. Mais cela ne suffit pas expliquer le battage
mdiatique qui a eu lieu autour de Java. Lincroyable succs de Java, durant les premires annes
de son existence, comme nous lavons mentionn au Chapitre 1, provient des applets. Un applet
est un type spcial de programme quun navigateur Web qui accepte Java peut tlcharger sur le
Net et excuter. Les utilisateurs devaient ainsi tre librs des tracasseries de linstallation dun
logiciel et allaient pouvoir avoir accs au logiciel depuis tout ordinateur ou priphrique Java
possdant une connexion Internet.

Livre Java .book Page 554 Jeudi, 25. novembre 2004 3:04 15

554

Au cur de Java 2 - Notions fondamentales

Pour plusieurs raisons, les applets nont jamais vraiment rpondu ces attentes. Sun a rcemment
dvelopp une approche alternative la livraison dapplications Internet, appele Java Web Start, qui
corrige certains problmes des applets.
Ce chapitre vous initie lcriture et au dploiement dapplets et dapplications Java Web Start, au
packaging dapplets et dapplications en vue du dploiement et la manire dont les applications
accdent aux informations de configuration et les stockent.

Introduction aux applets


Vous utilisez le HTML pour dcrire la mise en page dune page Web. HTML (Hypertext Markup
Language, "langage de balisage hypertexte") ne permet que dindiquer les lments dune page
hypertexte. Par exemple, <title> indique un titre de page : le texte qui suit cette balise apparatra en titre de la page. La fin du titre est marque par la balise </title> (cest une des rgles
gnrales des balises : une barre de fraction suivie par le nom de llment indique la fin de
llment).
Rien de plus simple au dpart que dinsrer un applet dans une page Web : on prcise en HTML au
navigateur les applets charger et lemplacement dans la page Web de chacun de ces applets. Pour
utiliser un applet Java, la balise Java de HTML doit ncessairement communiquer au navigateur
(que lon suppose compatible avec Java) les informations suivantes :
m

lemplacement des fichiers de classes ;

la faon dont un applet est install dans une page Web (dimension, emplacement, etc.).

Le navigateur rcupre alors les fichiers de classes sur le Net (ou dans un rpertoire de la machine de
lutilisateur) et lance automatiquement lexcution de lapplet grce un environnement dexcution
Java externe ou sa machine virtuelle Java.
Une page Web peut contenir, en plus dun applet, nimporte quel autre lment HTML visible sur
le Web : diffrentes polices de caractres, des listes puces, des lments graphiques, des
liens, etc. Les applets ne forment quune partie dune page hypertexte. Noubliez jamais que Java
nest pas un outil de conception de pages HTML : cest un outil pour les animer. Nous ne minimisons pas limportance des lments qui dfinissent linterface graphique utilisateur dans un
applet Java, mais ils sont strictement au service de lorganisation gnrale en HTML de la page
Web.
INFO
Nous ne prsentons pas ici les balises HTML : nous supposons que vous avez une connaissance lmentaire de
ce langage ou que vous travaillez avec quelquun qui les possde. Les applets Java nutilisent que peu de balises
HTML spcialises, qui seront bien entendu prsentes dans ce chapitre. Il existe quantit de livres sur HTML et
nous pouvons en conseiller un trs bien conu : HTML 4 de Michel Dreyfus (CampusPress Student Edition).

Quand les applets sont apparus, il fallait un navigateur HotJava de Sun pour visualiser les pages Web
qui les contenaient. Peu dutilisateurs sembarrassaient dun second navigateur uniquement pour
exploiter une nouvelle fonctionnalit du Web. Les applets de Java ne sont devenus populaires que

Livre Java .book Page 555 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

555

lorsque Netscape incorpora une machine Java virtuelle dans son navigateur, suivi rapidement par
Internet Explorer. Toutefois, deux problmes sont survenus. Netscape ne suivait pas les versions plus
modernes de Java et Microsoft hsitait entre supporter sans enthousiasme les versions obsoltes de
Java et abandonner carrment la prise en charge.
Pour surmonter ce problme, Sun a sorti un outil appel "Java Plug-in". Grce aux diffrents mcanismes dextension dInternet Explorer ou de Navigator, il sintgre en toute homognit dans
Netscape et Internet Explorer et leur permet dexcuter des applets Java dans un environnement
dexcution Java externe fourni par Sun. En maintenant le plug-in jour, vous avez la garantie de
toujours utiliser les caractristiques les plus rcentes et les plus importantes de Java.
INFO
Pour excuter les applets de ce chapitre dans un navigateur, vous devrez installer la version actuelle du plug-in Java
et vrifier que votre navigateur est connect au plug-in. Accdez ladresse http://java.sun.com/getjava pour obtenir
des informations de tlchargement et de configuration.

A lvidence, si vous destinez vos pages Web au grand public, vous ne pouvez gure demander aux
visiteurs de votre site dinstaller ce plug-in, quil sera fastidieux de tlcharger pour cette seule occasion. Avant de passer aux applets, vrifiez si vous pouvez utiliser les formulaires HTML, le JavaScript et des fichiers GIF pour implmenter linterface utilisateur client. Placez ensuite lintelligence
sur le serveur, de prfrence avec des servlets Java et des pages serveur.
Si vous avez conu des programmes Java plus sophistiqus, valuez la pertinence de les diffuser
travers un navigateur Web. Peut-tre suffirait-il de diffuser des applications Java excutables sur la
machine mme de lutilisateur. Vous profiterez alors de tous les avantages de Java, comme de lindpendance vis--vis des plates-formes ou de laccs facile aux bases de donnes et aux rseaux. Le
recours au Web conserve certains avantages. Pour lutilisateur, il est souvent plus facile de trouver
une application sur le serveur Web que dans un systme de fichiers (tout spcialement pour des
applications rarement utilises). Pour un administrateur, il est plus ais de maintenir et de mettre jour
une application sur le Web que de diffuser des corrections de bogues et de nouvelles versions
plusieurs machines clientes.
Cela justifie que les applications dentreprises sur intranet, interfaces avec des bases de donnes
dentreprise, figurent parmi les programmes Java les plus exploits. Ainsi, beaucoup de socits ont
install sur leur intranet des calculatrices pour notes de frais, des outils doptimisation des bnfices,
des organiseurs, chanciers et planifieurs de vacances, des saisies de bons de commandes, etc. Ces
programmes sont relativement petits. Ils doivent tre interfacs avec des bases de donnes, exigent
plus de souplesse que ne peut en apporter normalement un formulaire Web, et doivent tre adapts
aux rgles de lentreprise. Les applets et les mcanismes Java Web Start conviennent parfaitement
la distribution de ce type de programme, et, comme il sagit dune population captive, le problme de
linstallation de lexcution Java na plus lieu dtre.
Nous commencerons par les applets, la fois pour respecter la tradition et parce que leur comprhension permet de bien sacclimater la technologie Java Web Start.

Livre Java .book Page 556 Jeudi, 25. novembre 2004 3:04 15

556

Au cur de Java 2 - Notions fondamentales

Un petit applet
Pour respecter la tradition, nous allons transformer en applet le programme NotHelloWorld du
Chapitre 7. Du point de vue du programmeur, il ny a rien de mystrieux dans un applet. Il sagit
simplement dune classe Java qui tend la classe java.applet.Applet. Bien que le package
applet nappartienne pas au package AWT, un applet est un composant AWT, comme lillustre la
chane des hritages de la Figure 10.1. Dans ce livre, nous utilisons Swing pour implmenter les
applets. Tous nos applets tendent la classe JApplet, qui est la superclasse des applets de Swing.
On voit sur la Figure 10.1 que JApplet est une sous-classe immdiate de la classe standard
Applet.
Figure 10.1
Le diagramme
dhritage.

Object

Component

Container

Window

Panel

Frame

Applet

JFrame

JApplet

Livre Java .book Page 557 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

557

INFO
Si lapplet contient des composants Swing, il faut tendre la classe JApplet. En effet, des composants Swing situs
lintrieur dun applet ordinaire sont incapables de se redessiner correctement.

LExemple 10.1 donne le code de la version applet de Not hello world.


Remarquez les similitudes entre ceci et le programme correspondant du Chapitre 7. Ici, lapplet
se trouvant dans une page Web, il est inutile de prciser une mthode pour sortir de lapplet.
Exemple 10.1 : NotHelloWorldApplet.java
/*
Les balises HTML suivantes sont ncessaires pour afficher cet applet
dans un navigateur:
<applet code="NotHelloWorldApplet.class" width="300" height="100">
</applet>
*/
import javax.swing.*;
public class NotHelloWorldApplet extends JApplet
{
public void init()
{
JLabel label = new JLabel("Not a Hello, World applet",
SwingConstants.CENTER);
add(label);
}
}

Affichage des applets


Pour ltape suivante, afin de construire un applet oprationnel dans une page Web, dans le cas de ce
programme, il vous faudra au minimum :
m

compiler ce fichier .java en un fichier de classes ;

crer un fichier HTML pour indiquer au navigateur le fichier charger ainsi que les dimensions
de lapplet.

Il est dusage de donner aux fichiers HTML le mme nom que celui de la classe applet quil contient,
dans notre cas NotHelloWorldApplet.html. Voici son contenu :
<applet code = "NotHelloWorldApplet.class" width="300" height="300">
</applet>

Avant de visualiser lapplet dans un navigateur, une bonne ide consiste le tester dans le visualisateur dapplet, programme qui fait partie du JDK (Java Development Kit, cest--dire "kit de dveloppement logiciel"). Pour lutiliser, dans notre exemple, saisissez :
appletviewer NotHelloWorldApplet.html

Livre Java .book Page 558 Jeudi, 25. novembre 2004 3:04 15

558

Au cur de Java 2 - Notions fondamentales

dans la ligne de commande. Notez que dans largument de ligne de commande du programme visualisateur on cite le fichier HTML et non le fichier de classes. La Figure 10.2 prsente le visualisateur
affichant notre applet.
Figure 10.2
Affichage dun applet
dans un navigateur.

ASTUCE
Vous pouvez galement excuter les applets depuis votre diteur ou depuis un environnement intgr. Dans
Emacs, slectionnez JDE/Excuter Applet depuis le menu. Dans Eclipse, slectionnez Run/Run as/Java Applet dans
le menu.

INFO
Voici une astuce un peu bizarre pour viter le fichier HTML supplmentaire. Ajoutez une balise dapplet comme
commentaire lintrieur du fichier source :
/*
<applet code="MyApplet.class" width="300" height="300">
</applet>
*/
public class MyApplet extends JApplet
. . .

Puis, excutez le visualisateur dapplet comme fichier source en tant quargument de la ligne de commande :
appletviewer NotHelloWorldApplet.java

Il nest pas recommand den faire une pratique courante, mais cette procdure peut tre utile si vous dsirez minimiser le nombre de fichiers que vous devez traiter en phase de test.

Le visualisateur dapplet est adapt la premire phase de test, mais par la suite vous devrez
excuter vos applets dans un navigateur de faon les voir fonctionner tout comme un utilisateur.
En particulier le visualisateur dapplet ne vous montre que lapplet, priv de son environnement
de texte HTML. Si un fichier HTML contient plusieurs applets, le visualisateur affiche autant de
fentres.
Pour afficher correctement lapplet, il vous faut un navigateur Java 2. Aprs avoir install et configur le plug-in Java, il vous suffit alors de charger le fichier HTML dans le navigateur (voir
Figure 10.3). Si lapplet ne saffiche pas, cela signifie probablement que votre navigateur utilise sa
machine virtuelle intgre et que vous devez la configurer pour utiliser le Plug-in Java.

Livre Java .book Page 559 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

559

ASTUCE
Si vous modifiez votre applet et que vous le recompiliez, vous devez lancer le navigateur, de sorte quil charge les
nouveaux fichiers de classe. Actualiser la page HTML ne chargera pas le nouveau code. Cest un problme lorsque
vous dboguez un applet. Vous pouvez viter davoir redmarrer le navigateur en lanant la console Java et en
mettant la commande x, qui efface le cache du chargeur de classes. Vous pouvez ensuite charger nouveau la page
HTML, pour utiliser le nouveau code de lapplet. La console Java peut tre lance dans Netscape et Mozilla partir
du menu. Sous Windows, ouvrez la console du Plug-in Java dans le Panneau de configuration et demandez laffichage de la Console Java.

INFO
Avec les anciens navigateurs (en particulier Netscape 4), vous devez remplacer les balises applet par un objet
spcial ou par des balises embed afin de faire charger le plug-in par le navigateur. La page http://java.sun.com/
j2se/5.0/docs/guide/plugin/developer_guide/html_converter.html dcrit cette procdure et vous permet de
tlcharger un outil qui automatise la conversion de la page Web. La conversion nest plus ncessaire avec les
navigateurs modernes.

Si le plug-in Java na pas t install, votre navigateur devrait vous assister dans les tapes de sa mise
en place.
Figure 10.3
Visualiser un applet
dans un navigateur.

Conversion dune application en applet


Une application graphique Java (cest--dire une application utilisant AWT, que lon peut lancer par
le lanceur de programme java) se convertit facilement en un applet qui pourra tre incorpor une
page Web. Le code de linterface utilisateur reste presque inchang.
Voici les tapes ncessaires cette conversion :
1. Crez une page HTML avec la balise approprie au chargement du code de lapplet.
2. Crez une sous-classe de JApplet. Rendez-la publique, faute de quoi lapplet ne serait pas
charg.

Livre Java .book Page 560 Jeudi, 25. novembre 2004 3:04 15

560

Au cur de Java 2 - Notions fondamentales

3. Supprimez la mthode main de lapplication. Ne crez pas de cadre pour lapplication, elle
saffichera dans le navigateur.
4. Dplacez tout code dinitialisation depuis le constructeur de cadre vers la mthode init de
lapplet. Inutile de construire explicitement lobjet applet le navigateur linstancie votre
place et appelle la mthode init.
5. Eliminez lappel setSize. Pour les applets, le dimensionnement dpend des paramtres width
et height du fichier HTML.
6. Enlevez lappel to setDefaultCloseOperation. Un applet ne peut pas tre ferm. Il se
termine lorsquon quitte le navigateur.
7. Si lapplication appelle setTitle, supprimez lappel la mthode. Les applets nont pas de
barre de titre (mais rien ne vous empche de donner un titre la page Web en tant que telle via la
balise HTML <title>).
8. Nappelez pas setVisible(true), lapplet saffiche automatiquement.
INFO
Plus loin dans ce chapitre, vous verrez comment implmenter un programme qui est la fois un applet et une application.

A titre dexercice, essayez de transformer lapplication calculatrice du Chapitre 9 en applet. La


Figure 10.4 montre laspect du rsultat lintrieur dune page Web.
Figure 10.4
Un applet calculatrice.

LExemple 10.2 prsente la page HTML. Remarquez lajout dun texte aux balises de lapplet.

Livre Java .book Page 561 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

561

Exemple 10.2 : Calculator.html


<html>
<head><title>A Calculator</title></head>
<body>
<p>Here is a calculator, just in case you cant find yours.</p>
<applet code="CalculatorApplet.class" width="180" height="180">
</applet>
</body>
</html>

LExemple 10.3 contient le code de lapplet. Nous avons introduit une sous-classe JApplet, plac le
code dinitialisation dans la mthode init et limin les appels setTitle, setSize, setDefaultCloseOperation et setVisible. La classe CalculatorPanel est tire du Chapitre 9 et son code a
t omis.
Exemple 10.3 : CalculatorApplet.java
import java.awt.*;
import javax.swing.*;
public class CalculatorApplet extends JApplet
{
public void init()
{
CalculatorPanel panel = new CalculatorPanel();
add(panel);
}
}
java.applet.Applet 1.0

void init()

Est appele au chargement initial de lapplet. Surcharger cette mthode et y placer le code
dinitialisation.

void reSize(int width, int height)

Modifie la taille de lapplet. Cette mthode serait parfaite si elle fonctionnait correctement dans
une page Web. Mais les navigateurs ne la laissent pas interfrer avec leurs mcanismes de mise
en page.

Le cycle de vie dun applet


Les quatre mthodes init(), start(), stop() et destroy() de la classe Applet fournissent le
cadre de construction de tout applet srieux. Nous dcrivons brivement ces mthodes en indiquant
lendroit o les appeler et la nature du code quelles vont contenir.

init

Cette mthode rassemble toutes les initialisations ncessaires lapplet. Elle est appele aprs
que les balises param de la balise applet ont t traites. En principe, un applet devra traiter les
valeurs param et ajouter des composants linterface utilisateur.

Livre Java .book Page 562 Jeudi, 25. novembre 2004 3:04 15

562

Au cur de Java 2 - Notions fondamentales

Les applets possdent ventuellement un constructeur par dfaut, mais lusage est deffectuer
toutes les initialisations dans la mthode init et non dans le constructeur par dfaut.

start

Cette mthode est appele automatiquement aprs lappel par le navigateur de la mthode init.
Elle est appele de nouveau chaque fois que lutilisateur revient la page contenant lapplet
aprs avoir parcouru dautres pages. Notez que la mthode start peut tre appele plusieurs
fois, la diffrence de la mthode init. Par consquent, le code ne devant tre excut quune
seule fois doit se trouver dans la mthode init et non dans la mthode start. La mthode
start va relancer un thread de lapplet, par exemple pour ractiver une animation. Il est inutile
dimplmenter cette mthode (ni la mthode stop) si lapplet ne contient rien quil soit ncessaire de suspendre.

stop

Cette mthode est appele automatiquement quand lutilisateur quitte la page sur laquelle se
trouve lapplet. Elle peut donc tre appele plusieurs fois pour le mme applet. Son but est de
permettre larrt dune activit gourmande en temps UC, qui ralentit le systme lorsque lutilisateur ne sintresse pas lapplet. Vous implmenteriez cette mthode pour arrter une application
ou une reproduction audio.

destroy

Cette mthode nest appele que lorsque le navigateur sarrte normalement. Puisquun applet
est associ une page HTML, vous navez pas normalement laisser des ressources derrire
vous aprs quun utilisateur a quitt la page qui contient lapplet. Si vous le faites, vous pouvez
fermer les ressources en remplaant la mthode destroy.
java.applet.Applet 1.0

void start()

Surcharge cette mthode avec le code devant tre excut chaque fois que lutilisateur visite la
page du navigateur contenant lapplet. Exemple daction : relancer un thread.

void stop()

Surcharge cette mthode avec le code devant tre excut chaque fois que lutilisateur visite la
page du navigateur contenant lapplet. Exemple daction : dsactiver un thread.

void destroy()

Surcharge cette mthode avec le code lorsque lutilisateur quitte le navigateur.

Premires rgles de scurit


Les applets sont conus pour tre tlchargs depuis un site distant et excuts localement. Les
proccupations de scurit sont donc fondamentales : si lutilisateur active Java sur son navigateur,
le code de lapplet sera entirement tlcharg avec la page Web et excut immdiatement. Lutilisateur naura jamais la moindre possibilit dautoriser ou dinterdire lexcution de tel ou tel applet.
De ce fait, les applets, la diffrence des applications, ont des possibilits dexcution restreintes.
Le gestionnaire de scurit des applets provoque une SecurityException chaque fois quun applet
essaye de violer lune des rgles daccs.
Que reste-t-il de possible pour un applet sur toute plate-forme ? Il peut afficher des images, mettre
des sons, traiter les frappes au clavier et les clics de souris de lutilisateur et envoyer ce que lutilisateur

Livre Java .book Page 563 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

563

a saisi lhte do provient lapplet. Ces fonctionnalits suffisent, par exemple, pour donner des
informations et les illustrer, ou encore pour permettre lutilisateur de passer une commande. Mais
il est impossible aux applets de sortir de la "bote sable" pour modifier le systme de lutilisateur
ou pour lespionner. Dans ce chapitre, nous ne nous intressons quaux applets qui sexcutent
lintrieur de la bote sable.
En particulier, pour la plupart des navigateurs, lexcution dans la bote sable produit les effets
suivants :
m

Un applet ne peut jamais lancer un programme excutable localement.

Un applet ne peut pas communiquer avec un hte diffrent du serveur do il provient (ce serveur
est appel lhte dorigine). Les utilisateurs dapplet sont protgs des applets qui pourraient
tenter despionner les ressources intranet.

Un applet ne peut ni lire ni crire dans le systme de fichiers local.

Un applet ne peut obtenir aucune information sur lordinateur local lexception de la version
de Java utilise, du nom et de la version du systme dexploitation et des caractres servant
sparer les noms de fichiers (par exemple, / ou \), les chemins (comme : ou ;) et les lignes
(comme \n ou \r\n). En particulier, un applet ne peut pas obtenir le nom de lutilisateur, son
adresse e-mail, etc.

Toutes les fentres ouvertes par un applet affichent un message davertissement.

Tout cela nest possible que parce que les applets sont excuts par la machine virtuelle Java et non
pas directement par lUC de lordinateur de lutilisateur. Dans la mesure o linterprteur vrifie
toutes les instructions critiques et toutes les parties dangereuses du programme, un applet malfaisant
(ou peut-tre crit maladroitement) ne devrait pas planter lordinateur, ni craser la mmoire du
systme, ni modifier les droits accords par le systme dexploitation.
Ces restrictions sont trop fortes dans certains cas. Ainsi, dans un intranet dentreprise, un applet
devrait pouvoir accder aux fichiers locaux. Pour moduler les niveaux de scurit en fonction des
situations, on peut utiliser des applets certifis. Un applet certifi transporte un "certificat scuris"
qui donne lorigine de lapplet. Des techniques cryptographiques garantissent quun tel certificat ne
peut pas tre falsifi. Si vous avez confiance dans le signataire dun applet, il est possible de lui attribuer
des droits supplmentaires. Lauthentification des certificats est traite au Volume 2.
Si vous avez confiance dans le signataire dun applet, vous pouvez demander au navigateur dattribuer plus de droits lapplet, par exemple, ceux de votre intranet dentreprise ou faire confiance
ceux du site www.cracker.com. Le modle paramtrable de scurit de Java permet de moduler les
niveaux de droits selon les besoins. Un applet dans laquelle on a entirement confiance peut disposer
des mmes niveaux de droits que les applications locales. Les logiciels issus de socits plus ou
moins fiables ne recevront que certains droits. Les applets dorigine inconnue seront confins la
bote sable.
INFO
Les applications Java Web Start (vues plus loin dans ce chapitre) ont une bote sable plus adaptable. Il est possible
daccder certaines ressources systme, condition que lutilisateur du programme soit daccord.

Livre Java .book Page 564 Jeudi, 25. novembre 2004 3:04 15

564

Au cur de Java 2 - Notions fondamentales

En rsum, Java possde trois mcanismes distincts pour imposer la scurit :


m

Le programme est interprt par la machine virtuelle de Java et non excut directement.

Un gestionnaire de scurit contrle toutes les oprations risque travers la bibliothque


dexcution de Java.

Les applets peuvent tre certifis pour en identifier lorigine.


INFO

Le modle de scurit de la technologie ActiveX de Microsoft repose uniquement sur la troisime option. Si vous
devez excuter un contrle ActiveX, il vous faut lui faire compltement confiance. Ce modle est parfait lorsquil
nexiste quun nombre limit de fournisseurs de confiance, mais il nest pas adapt lchelle du Web. Si vous utilisez
Internet Explorer, vous observerez le fonctionnement du mcanisme des ActiveX. Vous devrez, par exemple, accepter
le certificat de Sun pour installer le plug-in sur Internet Explorer. Ce certificat indique que le code provient de Sun,
mais ne livre aucun dtail sur la qualit du programme. Ds lors que vous acceptez linstallation, le programme
sexcute sans aucune vrification de scurit ultrieure.

Fentres pop-up dans un applet


Un applet se trouve sur une page Web dans un cadre dont la taille est fixe par les valeurs width
et height des balises applet, ce qui peut reprsenter une contrainte gnante. On peut vouloir
utiliser une fentre pop-up pour faire un meilleur emploi de lespace disponible. Il suffit de
crer un cadre pop-up. Voyons lexemple dun petit applet possdant un bouton marqu
"Calculator" (Calculatrice), qui a pour effet de faire apparatre une calculatrice dans une fentre
distincte :
frame = new CalculatorFrame();
frame.setTitle("Calculator");
frame.setSize(200, 200);

Lorsque vous cliquez sur le bouton "Calculator", la bote de dialogue apparat sur la page Web. Un
nouveau clic sur ce bouton la fait disparatre :
JButton calcButton = new JButton("Calculator");
calcButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
frame.setVisible(!frame.isVisible());
}
});

Il existe cependant un pige que vous devez connatre avant de dposer votre applet sur votre
page Web. Pour vrifier quoi ressemble la calculatrice aux yeux dun utilisateur potentiel, chargez la page Web depuis un navigateur, et non pas dans le visualisateur dapplet. La calculatrice
sera entoure dune bordure accompagne dun message davertissement de mauvais augure
(voir Figure 10.5).

Livre Java .book Page 565 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

565

Figure 10.5
Une fentre pop-up
dans un navigateur.

Dans les navigateurs de premire gnration, ce message tait de mauvais prsage : Fentre dapplet
Java non scurise. Les versions successives ont tempr progressivement lavertissement : Fentre
dapplet Java non authentifie, ou Attention : fentre dapplet Java. Maintenant, on lit seulement :
Fentre dapplet Java.
Ce message est une mesure de scurit systmatique sur les navigateurs Web. Le navigateur veut
sassurer que votre applet ne lance pas une fentre pouvant tre confondue avec une application
locale par lutilisateur. Cette mesure permet dviter le cas o un utilisateur sans mfiance visiterait
une page Web lanant automatiquement un applet, taperait par inadvertance son mot de passe ou le
numro de sa carte de crdit, que lapplet naurait plus qu expdier son hte.
Pour viter toute possibilit de ce genre, les fentres pop-up lances par une applet contiennent
toutes un message du genre "Applet Java non fiable", "Applet Java non certifie" ou "Avertissement :
fentre provenant dun applet". Ce message aura de quoi pouvanter la plupart des utilisateurs, et par
consquent, vous viterez de crer un cadre externe depuis votre applet.
Vous pouvez configurer le plug-in Java pour liminer ces messages davertissement de fentres pop-up
issus dapplets signs.
LExemple 10.4 montre le code de la classe PopupCalculatorApplet.
Exemple 10.4 : PopupCalculatorApplet.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PopupCalculatorApplet extends JApplet
{
public void init()
{
// crer un cadre avec un panneau de calculatrice
final JFrame frame = new JFrame();
frame.setTitle("Calculator");

Livre Java .book Page 566 Jeudi, 25. novembre 2004 3:04 15

566

Au cur de Java 2 - Notions fondamentales

frame.setSize(200, 200);
frame.add(new CalculatorPanel());
// ajouter un bouton qui affiche ou masque la calculatrice
JButton calcButton = new JButton("Calculator");
add(calcButton);
calcButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
frame.setVisible(!frame.isVisible());
}
});
}
}

Balises HTML et attributs pour applets


Dans sa forme la plus simple, un exemple dutilisation de la balise applet ressemble ceci :
<applet code="NotHelloWorldApplet.class" width="300" height="100">

Nous avons vu que la balise code donne le nom du fichier de classes (lextension .class est obligatoire). Les balises width et height servent dimensionner la fentre qui va contenir lapplet (les
dimensions sont exprimes en pixels). Il vous faut galement une balise </applet> pour marquer la
fin du balisage HTML qui lui est destin. Le texte situ entre les balises <applet> et </applet> ne
sera affich que si le navigateur est incapable dafficher les applets. Les attributs code, width et
height sont ncessaires. Si une seule manque, le navigateur ne pourra pas charger lapplet.
Toutes ces informations sont normalement places dans une page HTML dont voici une esquisse :
<html>
<head>
<title>NotHelloWorldApplet</title>
</head>
<body>
<p>La ligne de texte qui suit sera affiche par Java:</p>
<applet code="NotHelloWorldApplet.class" width="100" height="100">
Aucun texte napparat ici dans le cas de navigateurs non
compatibles Java.
</applet>
</body>
</html>

INFO
Conformment la norme HTML, les balises HTML comme <applet> peuvent tre en majuscules ou en minuscules.
Nous utilisons des minuscules, puisque cela est recommand par la nouvelle spcification XHTML. Toutefois, la casse
est prise en compte pour reconnatre le nom dune classe applet, et ventuellement dautres attributs comme les
noms de fichiers JAR, lorsque le systme de fichiers du serveur Web distingue les majuscules des minuscules.

Livre Java .book Page 567 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

567

Les attributs de positionnement dun applet


A prsent, examinons brivement les attributs de positionnement de lapplet qui suivront la balise
applet. Les lecteurs familiariss avec le HTML reconnatront que la plupart de ces attributs sont
proches de ceux de la balise img qui installe une image dans une page Web.
width, height
Ces attributs sont obligatoires et dfinissent respectivement la largeur et la hauteur dun applet
(en pixels). Pour le visualisateur dapplet, il sagit de la taille initiale de lapplet. Sil est possible
de modifier les dimensions dun applet dans le visualisateur, cest impossible dans un navigateur.
Il faudra deviner lespace ncessaire un applet pour quil soit correctement vu dans tous les cas.
align
Cet attribut fixe lalignement dun applet. Deux possibilits principales : soit lapplet est
trait comme un bloc et le texte coule autour, soit lapplet est en ligne et se comporte dans
la ligne du texte comme le ferait un caractre de grande taille. Les deux premires valeurs
(left et right) font couler le texte autour de lapplet. Les autres intgrent lapplet dans le
texte.
Ces possibilits sont dtailles dans le Tableau 10.1.
Tableau 10.1 : Balises de positionnement dun applet

Attribut

Effet

left

Positionne lapplet contre la marge gauche de la page. Le texte qui fait


suite dans la page est dispos dans lespace laiss libre situ droite de
lapplet.

right

Positionne lapplet contre la marge droite de la page. Le texte qui fait suite
dans la page est dispos dans lespace laiss libre se trouvant gauche de
lapplet.

bottom

Aligne le bas de lapplet sur le bas du texte de la ligne courante.

top

Aligne le haut de lapplet sur le haut de la ligne courante.

texttop

Aligne le haut de lapplet sur le haut du texte de la ligne courante.

middle

Aligne le milieu de lapplet sur la ligne de base de la ligne courante.

absmiddle

Aligne le milieu de lapplet sur le milieu de la ligne courante.

baseline

Aligne le bas de lapplet sur la ligne de base de la ligne courante.

absbottom

Aligne le bas de lapplet sur le bas de la ligne courante.

vspace, hspace

Ces attributs facultatifs donnent le nombre de pixels au-dessus et en dessous de


lapplet (vspace) et de chaque ct de lapplet (hspace).

Livre Java .book Page 568 Jeudi, 25. novembre 2004 3:04 15

568

Au cur de Java 2 - Notions fondamentales

La Figure 10.6 indique toutes les possibilits dalignement dans le cas dun applet en fonction du
texte qui lentoure. La barre verticale localise au dbut de chaque ligne est une image plus haute
que le texte, ce qui permet de voir la diffrence entre lalignement sur le haut ou le bas de la ligne et
le haut ou le bas du texte.
Figure 10.6
Alignements possibles
dun applet.

Les attributs dapplet pour la partie Code


Les attributs dapplets suivants permettent au navigateur de trouver le code de lapplet.

Code

Cet attribut donne le nom du fichier de classes de lapplet. Ce nom est relatif lemplacement du
codebase (voir ci-aprs) ou de la page courante si le codebase nest pas spcifi. Le nom de
chemin doit alors correspondre la classe de lapplet. Par exemple, si la classe de lapplet se
trouve dans le groupe com.mycompany, lattribut est code="com/mycompany/MyApplet.class". Le code alternatif code="com.mycompany.MyApplet.class" est galement
autoris. Mais vous ne pouvez pas utiliser ici de chemin en absolu. Utilisez lattribut codebase
si votre fichier class se trouve ailleurs. Lattribut code spcifie seulement le nom de la classe qui
contient la classe de lapplet. Evidemment, votre applet peut contenir dautres fichiers de classes.
Une fois que le chargeur de classe du navigateur charge la classe contenant la classe de lapplet,
il ralise quil doit charger dautres fichiers classe, ce quil fera.
Lun des attributs code ou object doit tre prsent.

Livre Java .book Page 569 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

569

codebase

Cet attribut facultatif est une URL permettant de localiser les fichiers de classes. Vous pouvez
utiliser une URL absolue, mme sur un serveur diffrent. Le plus souvent, cependant, il sagit
dune URL relative qui pointe vers un sous-rpertoire. Par exemple si la disposition du fichier
ressemble ceci :
aDirectory/
MyPage.html
myApplets/
CalculatorApplet.class

Vous utilisez ensuite la balise suivante dans MyPage.html:


<applet code="CalculatorApplet.class" codebase="myApplets" width"100"
height="150">

archive

Cet attribut facultatif produit la liste du ou des fichiers darchives Java contenant les classes et les
autres ressources ncessaires lapplet (pour en savoir plus sur les fichiers darchives Java,
consultez la partie sur les fichiers JAR dans ce chapitre). Ces fichiers sont tlchargs depuis le
serveur Web avant le chargement de lapplet. Cette technique acclre le processus de chargement de faon sensible, car il suffit dune seule requte HTTP pour charger un unique fichier
JAR contenant beaucoup de fichiers plus petits. Les fichiers JAR sont spars par des virgules.
Par exemple :
<applet code="CalculatorApplet.class"
archive="CalculatorClasses.jar,corejava/CoreJavaClasses.jar"
width="100" height="150">

object

Cette balise permet de donner le nom dun fichier contenant un objet applet srialis. On dit
quun objet est srialis quand tous ses champs dinstance se trouvent dans un fichier. La srialisation est aborde au Chapitre 12. Pour afficher lapplet, lobjet devra tre dsrialis partir du
fichier afin de retrouver son tat antrieur. Dans le cas de cette balise, la mthode init nest pas
appele : cest la mthode start de lapplet qui est appel. Avant de srialiser un objet, il faut en
principe appeler sa mthode stop si lon souhaite implmenter un navigateur persistant qui
recharge automatiquement ses applets et les replace dans ltat o ils se trouvaient la fermeture
du navigateur. Cette contrainte est rare et il est inutile de sen proccuper.
Toute balise applet doit contenir lattribut code ou object. Par exemple,
<applet object="CalculatorApplet.ser" width="100" height="150">

name

Les transcriptions essaieront de donner lapplet un attribut name quelles puissent utiliser pour
se rfrer lapplet lors de la transcription. Netscape et Internet Explorer vous permettent
dappeler des mthodes dapplet sur une page laide de JavaScript. Cet ouvrage ntant pas
consacr JavaScript, nous ne prsentons que de brefs aspects du code ncessaire pour appeler
du code Java depuis JavaScript.
INFO
JavaScript est un langage de scriptage qui peut tre utilis dans une page Web (initialement appel LiveScript par
Netscape qui la conu). La syntaxe de ce langage est un peu analogue celle de Java, mais la ressemblance sarrte

Livre Java .book Page 570 Jeudi, 25. novembre 2004 3:04 15

570

Au cur de Java 2 - Notions fondamentales

l. Lappellation JavaScript est purement commerciale. Un sous-ensemble a t normalis (norme ECMA-262) sous le
nom particulirement accrocheur de ECMAScript. Comme il fallait sy attendre, les extensions du langage supportes
par les deux grands navigateurs sont incompatibles. Pour plus dinformation sur JavaScript, se reporter aux autres
ouvrages parus chez CampusPress.

Il faut commencer par donner un nom lapplet afin de pouvoir y accder :


<applet code="CalculatorApplet.class"
width="100" height="150" name="calc">
</applet>

On peut alors faire rfrence lobjet en tant que document.applets.nom de lapplet. Par exemple,
var calcApplet = document.applets.calc;

La magie de lintgration entre Java et JavaScript supporte par Netscape et Internet Explorer
permet lappel dune mthode de lapplet :
calcApplet.clear();

Il ne sagit que dun exemple de syntaxe puisque notre applet calculatrice ne possde pas de mthode
clear.
Lattribut name est indispensable quand deux applets dune mme page doivent communiquer directement. Il faut donner un nom chaque instance dapplet et le passer la mthode getApplet de la
classe AppletContext. Nous examinerons plus loin ce mcanisme de communication interapplets.
ASTUCE
Sur le site http://www.javaworld.com/javatips/jw-javatip80.html, Francis Lu utilise une communication JavaScriptvers-Java pour rsoudre un problme vieux comme le monde : redimensionner un applet de telle faon quil ne soit
pas li des valeurs figes des attributs width et height cods en dur. Cela constitue un bon exemple de lintgration entre Java et JavaScript. Cela montre galement combien il est difficile dcrire du JavaScript fonctionnant sur
plusieurs navigateurs concurrents.

Les attributs dun applet pour les visualisateurs acceptant Java


Un navigateur qui ne traite pas les applets Java va ignorer les balises applet et param dans une page
Web contenant la balise applet. Symtriquement, les navigateurs acceptant Java naffichent aucun
texte entre les balises <applet> et </applet>. Tout ce que vous pouvez faire pour les utilisateurs
dots dun navigateur antdiluvien, cest de placer un message entre ces deux balises :
<applet code="CalculatorApplet.class" width="100" height="150">
Si votre navigateur pouvait afficher Java, vous verriez ici
une calculatrice.
</applet>

Evidemment, la plupart des navigateurs acceptent aujourdhui Java, mais lutilisateur ou un administrateur systme paranoaque peut avoir dsactiv Java. Lattribut alt permet dafficher un message
ces infortuns :
<applet code="CalculatorApplet.class" width="100" height="150"
alt="Si votre navigateur pouvait afficher Java, vous verriez ici
une calculatrice">

Livre Java .book Page 571 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

571

La balise object
La balise object est dans la norme HTML 4.0 et devrait remplacer la balise applet selon le consortium W3. Elle possde 35 attributs, dont la plupart (comme onkeydown) ne sont utiles que pour
crire en Dynamic HTML. Les diffrentes balises de positionnement telles que align ou height
fonctionnent comme dans le cas de la balise applet. Lattribut essentiel de la balise object, dans le
cas dun applet Java, sappelle classid. Cet attribut donne lemplacement de lobjet. La balise
object permet le chargement de plusieurs autres types dobjets, par exemple de composants ActiveX,
tel le plug-in. Lattribut codetype spcifie la nature de lobjet, par exemple application/java
pour un applet Java :
<object
codetype="application/java"
classid="java:CalculatorApplet.class"
width="100" height="150">

Signalons que lattribut classid peut tre suivi par lattribut codebase qui fonctionne exactement
comme pour la balise applet.

Passer des informations un applet avec des paramtres


Les applets ont accs des paramtres localiss dans le fichier HTML comme le font les applications pour prendre des informations dans la ligne de commande. On utilise cet effet la balise
HTML param suivie dattributs dfinis librement. Par exemple, pour laisser une page Web redfinir
la police de caractres utilise par un applet :
<applet code="FontParamApplet.class" width="200", height="200">
<param name=font value="Helvetica"/>
</applet>

La mthode getParameter de la classe Applet rcupre la valeur du paramtre :


public class FontParamApplet extends JApplet
{
public void init()
{
String fontName = getParameter("font");
. . .
}
. . .
}

INFO
On ne peut appeler la mthode getParameter qu partir de la mthode init de lapplet en vitant surtout
de le faire partir du constructeur. A lexcution du constructeur, les paramtres ne sont pas encore disponibles.
Comme laspect de la plupart des applets un peu labors dpend de paramtres, nous suggrons de ne pas
crire de constructeur pour les applets, mais de placer tout simplement lensemble des initialisations dans la
mthode init.

Les paramtres sont toujours renvoys en tant que chanes de caractres quil faudra, le cas chant,
convertir en type numrique. Cette sparation seffectue via une mthode approprie telle que
parseInt de la classe Integer.

Livre Java .book Page 572 Jeudi, 25. novembre 2004 3:04 15

572

Au cur de Java 2 - Notions fondamentales

Par exemple, pour ajouter un paramtre de corps une police :


<applet code="FontParamApplet.class" width="200" height="200">
<param name="font" value="Helvetica"/>
<param name="size" value="24"/>
</applet>

Le source suivant montre la conversion du paramtre en entier :


public class FontParamApplet extends JApplet
{
public void init()
{
String fontName = getParameter("font");
int fontSize = Integer.parseInt(getParameter("size"));
. . .
}
}

INFO
Les chanes donnant les noms des paramtres via la balise param et celles quutilise la mthode getParameter
doivent tre identiques ; en particulier il faut distinguer majuscules et minuscules.

Il est possible de dterminer si le paramtre size a t effectivement utilis, en comparant la


longueur de la chane null:
int fontsize;
String sizeString = getParameter("size");
if (sizeString == null) fontSize = 12;
else fontSize = Integer.parseInt(sizeString);

Voici lexemple dun applet fort utile qui recourt abondamment des paramtres. Il trace cet histogramme (voir Figure 10.7).
Figure 10.7
Un applet construisant
un histogramme.

Livre Java .book Page 573 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

573

Cet applet trouve les libells et la hauteur des barres dans les valeurs param du fichier HTML.
Voici le fichier HTML correspondant la Figure 10.7 :
<applet code="Chart.class" width="400" height="300">
<param name="title" value="Diameters of the Planets"/>
<param name="values" value="9"/>
<param name="name.1" value="Mercury"/>
<param name="name.2" value="Venus"/>
<param name="name.3" value="Earth"/>
<param name="name.4" value="Mars"/>
<param name="name.5" value="Jupiter"/>
<param name="name.6" value="Saturn"/>
<param name="name.7" value="Uranus"/>
<param name="name.8" value="Neptune"/>
<param name="name.9" value="Pluto"/>
<param name="value.1" value="3100"/>
<param name="value.2" value="7500"/>
<param name="value.3" value="8000"/>
<param name="value.4" value="4200"/>
<param name="value.5" value="88000"/>
<param name="value.6" value="71000"/>
<param name="value.7" value="32000"/>
<param name="value.8" value="30600"/>
<param name="value.9" value="1430"/>
</applet>

Une autre faon de procder consiste placer dans lapplet un tableau de chanes de caractres et un
tableau de valeurs, mais nous voyons deux avantages lemploi du premier mcanisme avec des
param. Pour crer plusieurs copies du mme applet sur une page Web donnant des diagrammes diffrents, il suffit de placer deux balises applet avec des jeux de paramtres diffrents dans la mme
page. De plus, les donnes afficher peuvent tre modifies. Il va de soi que le diamtre des plantes
ne change pas rapidement, mais dans le cas dun diagramme de ventes annuelles, la mise jour de la
page Web se fait sans difficult puisque ce nest quun texte. Il serait bien plus fastidieux de modifier
le source et de recompiler un fichier Java chaque semaine.
Il existe des composants JavaBeans (beans Java) dans le commerce qui tracent des reprsentations
graphiques beaucoup plus labores que notre petit applet. Mais si vous en achetez un, vous pourrez
le placer dans une page Web et lui passer des paramtres sans avoir vous soucier de la faon dont
lapplet procde pour construire la reprsentation graphique.
LExemple 10.5 donne le source de notre applet. On remarque que les paramtres sont lus par la
mthode init et que cest la mthode paintComponent qui trace le diagramme.
Exemple 10.5 : Chart.java
import
import
import
import

java.awt.*;
java.awt.font.*;
java.awt.geom.*;
javax.swing.*;

public class Chart extends JApplet


{
public void init()
{
String v = getParameter("values");
if (v == null) return;
int n = Integer.parseInt(v);

Livre Java .book Page 574 Jeudi, 25. novembre 2004 3:04 15

574

Au cur de Java 2 - Notions fondamentales

double[] values = new double[n];


String[] names = new String[n];
for (int i = 0; i < n; i++)
{
values[i] = Double.parseDouble
(getParameter("value."+(i+1)));
names[i] = getParameter("name."+(i+1));
}
add(new ChartPanel(values, names,
getParameter("title")));
}
}
/**
Un panneau qui dessine un graphique en barres.
*/
class ChartPanel extends JPanel
{
/**
Construit un ChartPanel.
@param v Le tableau de valeurs pour le graphique
@param n Le tableau de noms pour les valeurs
@param t Le titre du graphique
*/
public ChartPanel(double[] v, String[] n, String t)
{
values = v;
names = n;
title = t;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (values == null || values.length == 0) return;
int iGraphics2D g2 = (Graphics2D)g;
// Calculer les valeurs minimum et maximum
if (values == null) return;
double minValue = 0;
double maxValue = 0;
for (double v: values)
{
if (minValue > v) minValue = v;
if (maxValue < v) maxValue = v;
}
if (maxValue == minValue) return;
int panelWidth = getWidth();
int panelHeight = getHeight();
Font titleFont = new Font("SansSerif", Font.BOLD, 20);
Font labelFont = new Font("SansSerif", Font.PLAIN, 10);
// Calculer ltendue du titre
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D titleBounds
= titleFont.getStringBounds(title, context);

Livre Java .book Page 575 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

double titleWidth = titleBounds.getWidth();


double top = titleBounds.getHeight();
// Dessiner le titre
double y = -titleBounds.getY(); // ascent
double x = (panelWidth - titleWidth) / 2;
g2.setFont(titleFont);
g2.drawString(title, (float) x, (float) y);
// Calculer ltendue des libells des barres
LineMetrics labelMetrics
= labelFont.getLineMetrics("", context);
double bottom = labelMetrics.getHeight();
y = panelHeight - labelMetrics.getDescent();
g2.setFont(labelFont);
// Obtenir le facteur chelle et la largeur des barres
double scale = (clientpanelHeight - top - bottom)
/ (maxValue - minValue);
int barWidth = panelWidth / values.length;
// Dessiner les barres
for (int i = 0; i < values.length; i++)
{
// Rcuprer les coordonnes du rectangle de la barre
double x1 = i * barWidth+1;
double y1 = top;
double height = values[i] * scale;
if (values[i] >= 0)
y1 += maxValue - values[i]) * scale;
else
{
y1 += maxValue * scale;
height = -height;
}
// Remplir la barre et dessiner son contour
Rectangle2D rect = new Rectangle2D.Double(x1, y1,
barWidth - 2, height);
g2.setPaint(Color.RED);
g2.fill(rect);
g2.setPaint(Color.BLACK);
g2.draw(rect);
// Dessiner le libell centr sous la barre
Rectangle2D labelBounds
= labelFont.getStringBounds(names[i], context);
double labelWidth = labelBounds.getWidth();
x = x1 +(barWidth - labelWidth) / 2;
g2.drawString(names[i], (float)x, (float)y);
}
}

575

Livre Java .book Page 576 Jeudi, 25. novembre 2004 3:04 15

576

Au cur de Java 2 - Notions fondamentales

private double[] values;


private String[] names;
private String title;
}
java.applet.Applet 1.0

public String getParameter(String name)

Prend la valeur dun paramtre dfini par la balise param dans la page Web ayant charg lapplet.
Attention aux majuscules/minuscules !

public String getAppletInfo()

Est une mthode surcharge par beaucoup dauteurs dapplets, qui renvoient ainsi dans une
chane leur nom, la version et le copyright de lapplet. Vous pouvez faire de mme en surchargeant
cette mthode dans votre classe applet.

public String[][] getParameterInfo()

Est une mthode que vous pouvez surcharger pour passer un tableau donnant les options de la
balise param supportes par lapplet. Chaque ligne contient trois entres : le nom du paramtre,
son type et une description. Par exemple :
"tps", "1-10", "frames per second"
"repeat", "boolean", "repeat image loop?"
"images", "url", "directory containing images"

Le multimdia
Les applets peuvent supporter les images comme les sons. A lheure actuelle, les images sont au
format GIF, PNG ou JPEG et les fichiers audio au format AU, AIFF, WAV ou MIDI. Les images GIF
animes sont acceptes et lanimation est effective. Gnralement, les fichiers contenant cette information sont spcifis en tant quURL, aussi allons-nous commencer par ces dernires.

Encapsuler les URL


Une URL nest rien dautre quune description sur le Net dune ressource. Par exemple, http://
java.sun.com/index.html demande au navigateur dutiliser le protocole de transfert hypertexte sur
le fichier index.html situ dans java.sun.com. Java possde la classe URL pour encapsuler les
URL. La faon la plus simple dobtenir une URL consiste transmettre une chane au constructeur
URL:
URL u = new URL("http://java.sun.com/index.html");

Cette URL est dite absolue parce que nous spcifions le nom complet de la ressource. Un autre
constructeur pratique dURL produit une URL relative :
URL data = new URL(u, "data/planets.dat");

Elle spcifie un fichier planets.dat situ dans le sous-rpertoire data de lURL u.


On obtient le plus souvent une URL en demandant un applet do il vient, plus prcisment :
m

Quelle est lURL de la page HTML contenant lapplet ?

Quelle est lURL du rpertoire du codebase de lapplet ?

Livre Java .book Page 577 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

577

Pour trouver la premire, utilisez la mthode getDocumentBase et, pour la seconde, employez
getCodeBase.
INFO
Dans les versions prcdentes du JDK, il existait une confusion importante sur ces mthodes : voyez le bogue
n 4456393 dans la parade des bogues Java (http://bugs.sun.com/bugdatabase/index.jsp). La documentation a
finalement t prcise pour le JDK 5.0.

INFO
Vous accderez aux pages Web scurises (URL https) partir des applets et grce au plug-in Java (voir http://
java.sun.com/products/plugin/1.3/docs/https/html). Cela utilise les capacits SSL du navigateur sous-jacent.

Rcuprer des fichiers multimdias


Les images et les fichiers audio peuvent tre obtenus grce aux mthodes getImage et getAudioClip. Par exemple :
Image cat = getImage(getCodeBase(), "images/cat.gif");
AudioClip meow = getAudioClip(getCodeBase(),
"audio/meow.au");

La mthode getCodeBase utilise dans ce cas renvoie lURL partir de laquelle le code dapplet est
charg. Le second argument des appels de mthode spcifie lemplacement de limage ou du clip
audio.
INFO
Les images et les clips audio doivent se trouver sur le mme serveur hbergeant le code de lapplet. Pour des raisons
de scurit, les applets ne peuvent accder aucun fichier sur un autre serveur (les applets ne peuvent tlphoner
que chez eux).

Au Chapitre 7, nous avons tudi laffichage dune image. Pour lancer un clip audio, il suffit den
invoquer la mthode play qui peut aussi tre appele avec la mthode play de la classe Applet sans
que le clip ait t charg au pralable :
play(getCodetBase(), "audio/meow.au");

Les objets multimdias peuvent tre placs dans des fichiers JAR pour amliorer le temps de chargement (voir la section ci-aprs). Les mthodes getImage et getAudioClip/play recherchent automatiquement les fichiers JAR de lapplet. Lorsque le fichier JAR contient une image ou un fichier
audio, ce dernier est charg immdiatement, dispensant ainsi le navigateur de le demander au
serveur Web.
java.net.URL 1.0

URL(String name)

Cre un objet URL partir dune chane dcrivant une URL absolue.

Livre Java .book Page 578 Jeudi, 25. novembre 2004 3:04 15

578

Au cur de Java 2 - Notions fondamentales

URL(URL base, String name)

Cre un objet URL relatif. Lorsque la chane name dcrit une URL absolue, lURL de base est
ignore. Sinon elle est interprte comme un rpertoire relatif lURL de base.
Java.applet.Applet 1.0

URL getDocumentBase()

Rcupre lURL de la page Web contenant lapplet.

URL getCodeBase()

Rcupre lURL du rpertoire de codebase partir duquel cet applet est charg. Il sagit de
lURL absolue du rpertoire rfrenc par lattribut codebase ou du rpertoire du fichier HTML
lorsque aucun codebase nest spcifie.

void play(URL url)


void play(URL url, String name)

La premire variante joue un fichier audio spcifi par lURL. La seconde utilise la chane pour
spcifier en premier paramtre un chemin relatif lURL. Si le clip audio ne peut tre trouv, il
ne se passe rien.

AudioClip getAudioClip(URL url)


AudioClip getAudioClip(URL url, String name)

Rcuprent un clip audio partir de lURL spcifie. La seconde version utilise la chane name
pour spcifier en premier argument un chemin relatif lURL. Les mthodes renvoient null
lorsque le clip audio est introuvable.

Image getImage(URL url)


Image getImage(URL url, String name)

Rcuprent une image lURL spcifie. Ces mthodes renvoient toujours immdiatement un
objet image, mme lorsque limage nexiste pas. Les donnes de limage ne sont en fait charges
quau premier affichage de limage.

Le contexte dapplet
Un applet sexcute lintrieur dun navigateur ou dun visualisateur dapplet. Un applet peut
demander au navigateur deffectuer certaines actions pour lui, comme rcuprer un clip audio, afficher un court message dans la barre dtat ou montrer une autre page Web. Le navigateur actif peut
traiter ces requtes ou bien les ignorer. Par exemple, si un applet sexcutant lintrieur du visualisateur dapplet demande au programme visualisateur dafficher une page Web, il ne se passe rien.
Un applet communique avec le navigateur actif laide de la mthode getAppletContext. Cette
mthode renvoie un objet qui implmente une interface de type AppletContext. Vous pouvez vous
reprsenter limplmentation de cette interface comme une voie de communication entre lapplet et
le navigateur actif. Cette interface contient en plus de getAudioClip et de getImage plusieurs
mthodes fort utiles que nous dcrivons ci-aprs.

La communication interapplets
Une page Web peut contenir plus dun applet. Lorsque ces applets proviennent dun mme codebase, ils peuvent communiquer ensemble. Mais cest une fonctionnalit avance dont vous naurez

Livre Java .book Page 579 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

579

sans doute pas souvent lusage. A condition de placer dans le fichier HTML des attributs name pour
chaque applet, on peut utiliser la mthode getApplet de linterface AppletContext pour obtenir
une rfrence lapplet. Par exemple, si le fichier HTML contient
<applet code="Chart.class" width="100" height="100" name="Chart1">

lappel suivant,
Applet chart1 = getAppletContext().getApplet("Chart1");

renvoie une rfrence lapplet. Que peut-on en faire ? En supposant que la classe Chart possde
une mthode pour entrer de nouvelles donnes et dessiner nouveau lhistogramme, on peut alors
lappeler condition deffectuer le transtypage voulu :
((Chart)chart1).setData(3, "Earth", 9000);

On peut galement lister lensemble des applets dans une page Web, sans quils aient ncessairement
une balise name. La mthode getApplets renvoie un objet enumeration. Voici une boucle qui affiche
les noms des classes de tous les applets situs dans la page courante :
Enumeration e = getAppletContext().getApplets();
while (e.hasMoreElements())
{
Object a = e.nextElement();
System.out.println(a.getClass().getName());
}

Les applets ne peuvent communiquer ensemble qu lintrieur de la mme page Web.

Faire afficher des informations par le navigateur


Vous pouvez vous servir de deux zones du navigateur actif : la barre dtat et la zone daffichage de
la page Web. Dans les deux cas, vous utiliserez les mthodes de la classe AppletContext.
Un message showStatus permet dafficher une chane dans la barre dtat situe dans le bas du navigateur. Par exemple :
showStatus("Loading data... please wait");

ASTUCE
A notre avis, showStatus est peu utile, parce que le navigateur exploite galement la barre dtat pour afficher
des messages sans aucun intrt comme "Applet en cours dexcution", lesquels craseront votre prcieux
message. Pour cette raison, nutilisez la barre dtat que pour des indications non essentielles, du genre "Chargement Merci de patienter".

La mthode showDocument demande au navigateur de montrer une autre page Web, ce qui peut tre
fait de plusieurs faons. La plus simple consiste appeler showDocument avec en argument lURL
afficher :
URL u = new URL("http://java.sun.com/indx.html");
getAppletContext().showDocument(u);

Linconvnient avec cet appel est que la nouvelle page Web est ouverte dans la mme fentre que
votre page courante, dplaant de ce fait votre applet, et contraignant lutilisateur cliquer sur
Prcdente pour y revenir.

Livre Java .book Page 580 Jeudi, 25. novembre 2004 3:04 15

580

Au cur de Java 2 - Notions fondamentales

On peut demander au navigateur de montrer le document dans une autre fentre en transmettant un
second paramtre dans lappel de showDocument (voir Tableau 10.2). Si vous fournissez la chane
spciale "_blank" [vide], lindicateur ouvre une nouvelle fentre contenant le document au lieu de
dplacer le document courant. Plus intressant encore, si vous vous servez des cadres HTML, vous
pourrez scinder une fentre de navigateur en plusieurs cadres, chacun dot dun nom. Lapplet peut
tre plac dans un cadre, et il affichera les documents dans les autres cadres. Nous verrons un exemple
de ce cas de figure dans la prochaine section.
INFO
Le visualisateur dapplets de Sun naffiche pas les pages Web. La mthode showDocument y est ignore.

Tableau 10.2 : La mthode showDocument

Paramtre cible

Emplacement

"_self" ou rien

Affiche le document dans le cadre courant.

"_parent"

Affiche le document dans le conteneur parent.

"_top"

Affiche le document dans le cadre se trouvant le plus haut.

"_blank"

Affichage dans une nouvelle fentre, sans nom, place au-dessus.

toute autre chane

Affichage dans le cadre dsign. Sil nexiste pas de cadre de ce nom, ouvre
une nouvelle fentre en y attribuant le nom donn.

java.applet.Applet 1.2

public AppletContext getAppletContext()

Renvoie un handle sur lenvironnement du navigateur de lapplet. Avec la plupart des navigateurs,
on peut utiliser cette information pour agir sur le navigateur o sexcute lapplet.

void showStatus(String msg)

Affiche la chane spcifie dans la barre dtat du navigateur.


java.applet.AppletContext 1.2

Enumeration getApplets()

Renvoie la liste de tous les applets dun mme contexte, cest--dire dune mme page Web.

Applet getApplet(String name)

Renvoie lapplet dsign qui se trouve dans le contexte courant, ou bien null sil nexiste pas.
La recherche nest faite que dans la page Web courante.

void showDocument(URL url)


void showDocument(URL url, String target)

Affiche une nouvelle page Web dans un cadre du navigateur. Dans la premire variante de cette
mthode, la nouvelle page remplace la page courante. La seconde variante utilise le paramtre
target pour identifier le cadre cible (voir Tableau 10.2).

Livre Java .book Page 581 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

581

Un applet signet
Cet applet utilise les cadres, une fonctionnalit de HTML. Nous divisons verticalement lcran en
deux cadres. Le cadre gauche contient un applet Java affichant une liste de signets. Par un doubleclic sur lun des signets de la liste, lapplet indique au navigateur dafficher la page Web correspondante
(voir Figure 10.8).
Figure 10.8
Un applet signet.

LExemple 10.6 donne le fichier HTML dfinissant les cadres.


Exemple 10.6 : Bookmark.html
<html>
<head>
<title>Bookmark Applet</title>
</head>
<frameset cols="320,*">
<frame name="left" src="Left.html"
marginheight="2" marginwidth="2"
scrolling="no" noresize="noresize"/>
<frame name="right" src="Right.html"
marginheight="2" marginwidth="2"
scrolling="yes" noresize="noresize"/>
</frameset>
</html>

Nous nallons pas passer en revue toute la syntaxe. Ce qui importe, cest de comprendre que chaque
cadre possde deux fonctionnalits essentielles : un nom (spcifi par la balise name) et une URL
(spcifie par la balise src). Nous avons appel les cadres respectivement "left" (gauche) et
"right" (droit).

Livre Java .book Page 582 Jeudi, 25. novembre 2004 3:04 15

582

Au cur de Java 2 - Notions fondamentales

Le cadre gauche utilise le fichier Left.html (voir Exemple 10.7), qui charge lapplet dans le cadre
gauche. Il spcifie simplement les applets et les signets. Vous pouvez personnaliser ce fichier pour
votre page Web en changeant les signets.
Exemple 10.7 : Left.html
<html>
<head><title>A Bookmark Applet</title></head>
<body>
<p>
Click one of the radio buttons.
The corresponding Web page will be displayed
in the frame on the right.
</p>
<applet code="Bookmark.class" width="290" height="300">
<param name=link.1 value="http://java.sun.com"/>
<param name=link.2 value="http://java.net"/>
<param name=link.3 value="http://linuxtoday.com"/>
<param name=link.4 value="http://www.horstmann.com"/>
<param name=link.5 value="http://www.phptr.com">
<param name=link.6 value="http://usps.com"/>
<param name=link.7 value="http://www.cafeaulait.org"/>
</applet>
</body>
</html>

Le cadre droit (voir Exemple 10.8) charge un fichier non fonctionnel, intitul Right.html (ce fichier
doit tre introduit parce que certains navigateurs nadmettent pas un cadre droit vide).
Exemple 10.8 : Right.html
<html>
<head><title>Web pages will be displayed here.</title></head>
<body>
<p>Click one of the radio buttons to the left.
The corresponding Web page will be displayed here.</p>
</body>
</html>

Le code de lExemple 10.9 de lapplet signet est trs simple. Il lit les valeurs des paramtres link.1,
link.2, etc. et les place dans la bote listes. Lorsque lon double-clique sur un lment de la bote
listes, la mthode showDocument affiche la page en question dans le cadre droit.
Exemple 10.9 : Bookmark.java
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.applet.*;
java.util.*;
java.net.*;
javax.swing.*;

public class Bookmark extends JApplet


public void init()
{
Box box = Box.createVerticalBox();
ButtonGroup group = new ButtonGroup();

Livre Java .book Page 583 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

583

int i = 1;
String urlString;
// lire tous les paramtres link.n
while ((urlString = getParameter("link."+i))!= null)
{
try
{
final URL url = new URL(urlString);
// crer un bouton radio pour chaque lien
JRadioButton button = new JRadioButton(urlString);
box.add(button);
group.add(button);
// choisir le bouton radio qui affiche lURL dans
// le cadre "droit"
button.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
{
AppletContext context = getAppletContext();
context.showDocument(url, "right");
}
});
}
catch(MalformedURLException e)
{
e.printStackTrace();
}
i++;
}
add(box);
}
}

Cest un applet et cest aussi une application !


Voici quelques annes, un comique la tlvision parodiait une publicit en montrant un couple en
train de se disputer propos dune substance blanche glatineuse. Le mari disait : "Cest pour napper
les desserts." Et sa femme : "Cest une cire parquet." Et lannonceur concluait triomphalement :
"Cest les deux la fois !"
Dans cette section, nous vous montrerons comment crire un programme Java qui soit la fois un
applet et une application. Cela signifie que vous pouvez charger le programme la fois avec le visualisateur dapplet ou avec le navigateur, ou encore le charger partir dune ligne de commande avec
le lanceur de programmes Java. Nous ne savons pas si cet usage est frquent, mais il est intressant
que vous en connaissiez lexistence.
Les Figures 10.9 et 10.10 montrent le mme programme, lanc depuis la ligne de commande et affich
lintrieur du visualisateur dapplet.

Livre Java .book Page 584 Jeudi, 25. novembre 2004 3:04 15

584

Au cur de Java 2 - Notions fondamentales

Nous allons voir comment procder. Un fichier de classes ne peut possder quune seule classe
publique. Pour que le visualisateur dapplet puisse le lancer, cette classe doit driver de Applet.
Pour que Java puisse lancer lapplication, il doit y avoir une mthode statique main. Nous avons
ainsi ce stade :
class MyAppletApplication extends JApplet
{
public void init() { . . . }
. . .
public static void main(String[] args) { . . . }
}

Figure 10.9
La calculette en tant
quapplication.

Figure 10.10
La calculette en tant
quapplet.

Que peut-on mettre dans main? Normalement, nous crons un objet instance de la classe et appelons sa mthode setVisible(true). Mais ce nest pas si simple dans ce cas, car on ne peut appeler
setVisible pour un applet nu. Lapplet doit tre plac dans un cadre.
Voici un exemple dune classe AppletFrame crant un cadre :
public class AppletFrame extends JFrame
{

Livre Java .book Page 585 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

585

Public AppletFrame(Applet anApplet)


{
applet = anApplet;
add(applet);
. . .
}
. . .
}

Le constructeur du cadre place lapplet (qui est un Component) lintrieur du volet conteneur du
cadre.
Dans la mthode main de lapplet/application, nous construisons et affichons un AppletFrame:
class MyAppletApplication extends JApplet
{
public void init() { . . . }
. . .
public static void main(String args[])
{
AppletFrame frame
= new AppletFrame(new MyAppletApplication());
frame.setTitle("MyAppletApplication");
frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}

Lorsque lapplet dmarre, il faut appeler ses mthodes init et start. Nous y parvenons en surchargeant
la mthode setVisible de la classe AppletFrame:
public void setVisible(boolean b)
{
if (b)
{
applet.init();
super.setVisible(true);
applet.start();
}
else
{
applet.stop();
super.setVisible(false);
applet.destroy();
}
}

Seulement, il y a un problme : si le programme est lanc par le lanceur Java et non par le visualisateur dapplet et quil appelle getAppletContext, il obtiendra un pointeur null parce quil na pas
t lanc dun navigateur. Cela provoque un plantage lexcution chaque fois que lon va rencontrer ce
genre dinstruction :
getAppletContext().showStatus(message);

Nous ne dsirons pas crire un programme de navigation complet, mais il nous faut fournir le minimum pour faire en sorte que les appels fonctionnent. Lappel naffiche pas de message, mais au
moins il ne fait pas planter le programme. Il nous suffit dimplmenter deux interfaces : AppletStub
et AppletContext.

Livre Java .book Page 586 Jeudi, 25. novembre 2004 3:04 15

586

Au cur de Java 2 - Notions fondamentales

Nous avons dj vu les contextes dapplet en action. Ils ont la charge de rcuprer les fichiers images
et audio ainsi que dafficher les pages Web. Ils peuvent cependant refuser de telles oprations,
comme dans notre cas. Le premier intrt de linterface AppletStub est de localiser le contexte
dapplet. Un applet possde toujours un applet stub (install en mme temps que la mthode setStub
de la classe Applet).
Dans notre cas, AppletFrame implmente la fois AppletStub et AppletContext. Nous napportons que le strict minimum des fonctionnalits ncessaires limplmentation de ces deux interfaces :
public class AppletFrame extends JFrame
implements AppletStub, AppletContext
{
. . .
// Mthodes AppletStub
public boolean isActive() { return true; }
public URL getDocumentBase() { return null; }
public URL getCodeBase() { return null; }
public String getParameter(String name) { return ""; }
public AppletContext getAppletContext() { return this; }
public void appletResize(int width, int height) {}
// Mthodes AppletContext
public AudioClip getAudioClip(URL url) { return null; }
public Image getImage(URL url) { return null; }
public Applet getApplet(String name) { return null; }
public Enumeration getApplets() { return null; }
public void showDocument(URL url) {}
public void showDocument(URL url, String target) {}
public void showStatus(String status) {}
public void setStream(String key, InputStream stream) {}
public InputStream getStream(String key) { return null; }
public Iterator getStreamKeys() { return null; }
}

INFO
A la compilation de ce fichier avec le compilateur du JDK 1.3, vous aurez un message davertissement signalant que
la classe java.awt.Window possde galement une mthode appele isActive qui est visible dans le package.
Puisque cette classe ne se trouve pas dans le mme package que la classe Window, elle ne peut surcharger la mthode
Window.isActive, ce qui nous convient fort bien : en effet, nous voulons ajouter une nouvelle mthode isActive linterface AppletStub. Et, aspect intressant, il est tout fait possible dajouter la sous-classe une
nouvelle mthode possdant la mme signature. Chaque fois quune rfrence Window situe dans le package
java.awt accde lobjet, la mthode de visibilit de package Window.isActive est appele. Cependant, cest la
mthode AppletFrame.isActive qui est appele lorsquun appel est fait via AppletFrame ou AppletStub.

Le constructeur de la classe cadre appelle ensuite setStub de lapplet pour crer lui-mme un stub :
public AppletFrame(Applet anApplet)
{
applet = anApplet
Container contentPane = getContentPane();
contentPane.add(applet);
applet.setStub(this);
}

Livre Java .book Page 587 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

587

Un dernier perfectionnement. Si nous souhaitons utiliser simultanment la calculatrice en tant


quapplet et en tant quapplication, plutt que de dplacer les mthodes de la classe CalculatorApplet dans la classe CalculatorAppletApplication, nous allons simplement recourir lhritage :
public class CalculatorAppletApplication extends CalculatorApplet
{
public static void main(String args[])
{
AppletFrame frame
= new AppletFrame(new CalculatorApplet());
. . .
}
}

Cette opration peut tre faite quel que soit lapplet et pas seulement pour notre calculatrice. Il suffit
simplement de faire driver de votre classe applet une classe MyAppletApplication et de passer
AppletFrame un nouvel objet MyApplet() dans la mthode main, ce qui donne une classe qui est
la fois un applet et une application.
Pour le plaisir, nous avons utilis lastuce mentionne prcdemment qui consiste ajouter la balise
applet en commentaire du fichier source sans avoir besoin daucun autre fichier HTML.
Les Exemples 10.10 et 10.11 listent le code. Il vous faut copier le fichier CalculatorApplet.java
dans le mme rpertoire afin de compiler le programme. Lancez lapplet et lapplication :
appletviewer CalculatorAppletApplication.java
java CalculatorAppletApplication

Exemple 10.10 : AppletFrame.java


import
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.applet.*;
java.io.*;
java.net.*;
java.util.*;
javax.swing.*;

public class AppletFrame extends JFrame


implements AppletStub, AppletContext
{
public AppletFrame(Applet anApplet)
{
applet = anApplet;
add(applet);
applet.setStub(this);
}
public void setVisible(true)
{
if (b)
{
applet.init();
super.setVisible(true);
applet.start();
}
else
{
applet.stop();

Livre Java .book Page 588 Jeudi, 25. novembre 2004 3:04 15

588

Au cur de Java 2 - Notions fondamentales

super.setVisible(false);
applet.destroy();
}
// Mthodes AppletStub
public boolean isActive() { return true; }
public URL getDocumentBase() { return null; }
public URL getCodeBase() { return null; }
public String getParameter(String name) { return ""; }
public AppletContext getAppletContext() { return this; }
public void appletResize(int width, int height) {}
// Mthodes AppletContext
public AudioClip getAudioClip(URL url) { return null; }
public Image getImage(URL url) { return null; }
public Applet getApplet(String name) { return null; }
public Enumeration getApplets() { return null; }
public void showDocument(URL url) {}
public void showDocument(URL url, String target) {}
public void showStatus(String status) {}
public void setStream(String key, InputStream stream) {}
public InputStream getStream(String key) { return null; }
public Iterator getStreamKeys() { return null; }
private Applet applet;
}

Exemple 10.11 : CalculatorAppletApplication.java


/*
Le visualisateur dapplets lit les balises ci-aprs si
vous lappelez avec
appletviewer CalculatorAppletApplication.java (!)
Aucun fichier HTML nest ncessaire.
<applet code="CalculatorAppletApplication.class"
width="200" height="200">
</applet>
*/
import javax.swing.*;
public class CalculatorAppletApplication
extends CalculatorApplet
// Cest un applet. Cest une application. Cest les DEUX!
{
public static void main(String[] args)
{
AppletFrame frame
= new AppletFrame(new CalculatorApplet());
frame.setTitle("CalculatorAppletApplication");
frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static final int DEFAULT_WIDTH = 200;
public static final int DEFAULT_HEIGHT = 200;
}

Livre Java .book Page 589 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

589

Les fichiers JAR


Lapplet calculatrice de ce chapitre utilise deux classes : CalculatorApplet et CalculatorPanel.
Nous savons que la balise applet fait rfrence au fichier de classes qui contient la classe applet:
<applet code="CalculatorApplet.class" width="100" height="150">

Lorsque le navigateur lit cette ligne, il se connecte au serveur Web et recherche le fichier CalculatorApplet.class. Le chargeur de classes de la machine virtuelle du navigateur charge la classe
CalculatorApplet de ce fichier. Lors du processus de chargement, le chargeur de classes doit
rsoudre les rfrences aux autres classes utilises par cette classe. Il sait alors quil a ventuellement besoin de plus dune classe pour excuter lapplet et, en ce cas, se connecte de nouveau au
serveur Web, un pour chaque fichier de classe. Le chargement dune applet peut alors ncessiter
plusieurs minutes pour peu que le rseau soit lent.
NOTE
Il faut bien comprendre que ce temps de chargement nest pas d la taille des fichiers de classes, relativement
petits, mais la surcharge considrable dcoulant dune connexion avec un serveur Web.

Java supporte maintenant une mthode amliore pour le chargement des fichiers de classes : elle
permet de rassembler tous les fichiers de classes ncessaires en un seul fichier. Ce fichier peut alors
tre charg grce une requte HTTP unique vers le serveur. Ces fichiers sont appels fichiers JAR
(acronyme de "Java Archive"). Ils peuvent contenir la fois des fichiers de classes et dautres types
de fichiers comme des fichiers images et audio. Les fichiers JAR peuvent tre galement compresss
au format de compression classique ZIP, ce qui rduit le temps de tlchargement. Loutil jar
permet de crer des fichiers JAR (dans linstallation par dfaut, il se trouve dans le rpertoire \jdk\
bin). Voici la commande la plus courante pour crer un nouveau fichier JAR :
jar cf JARFileName Fichier1 Fichier2 . . .

Exemple :
jar cvf CalculatorClasses.jar *.class icon.gif

La commande jar doit se conformer la syntaxe suivante :


jar options Fichier1 Fichier2 . . .

Le Tableau 10.3 donne toutes les options possibles pour le programme jar. Elles sont analogues aux
options de la commande UNIX tar.
Tableau 10.3 : Les options du programme jar

Option

Description

Cre une nouvelle archive vide et y place des fichiers. Si lun des noms de fichiers indiqus
est un rpertoire, le programme jar le traite de faon rcursive.

Affiche le contenu de larchive.

Met jour un fichier JAR existant.

Livre Java .book Page 590 Jeudi, 25. novembre 2004 3:04 15

590

Au cur de Java 2 - Notions fondamentales

Tableau 10.3 : Les options du programme jar (suite)

Option

Description

Extrait les fichiers. Si vous fournissez un ou plusieurs noms de fichiers, seuls ceux-ci
seront extraits.

Spcifie le nom du fichier JAR comme second argument de la ligne de commande.


Par dfaut, jar crit le rsultat sur la sortie standard (lors de la cration dun fichier JAR)
ou lit partir de lentre standard (lors de lextraction dun fichier JAR).

Sortie de messages dtaille.

Ajoute un fichier manifeste au fichier JAR. Un manifeste est la description du contenu et


de lorigine de larchive. Toute archive possde un manifeste par dfaut, mais vous pouvez
en fournir un spcial si vous souhaitez authentifier le contenu de larchive.

Stockage sans compression ZIP.

Ne cre pas de manifeste pour les entres.

Cre un fichier dindice (voir ci-aprs pour en savoir plus).

Change temporairement le rpertoire. Par exemple : jar cvf JARFileName.jar


-C classes *.class change en direction du sous-rpertoire classes pour ajouter les
fichiers classe.

Une fois le fichier JAR cr, il faudra y faire rfrence dans la balise applet de la faon suivante :
<applet code="CalculatorApplet.class"
archive="CalculatorClasses.jar"
width="100" height="150">

Lattribut code doit toujours tre prsent. Il indique au navigateur le nom de lapplet. archive dsigne simplement une source demplacement possible de la classe applet et des autres fichiers. Chaque
fois quun fichier de classes, dimages ou de son est requis, un navigateur acceptant les fichiers JAR
commence par rechercher ces fichiers JAR dans la liste archive. Si le ou les fichiers ne sont pas trouvs
dans larchive, ils seront alors recherchs sur le serveur Web.
ASTUCE
Si vous disposez dun grand applet, il est possible que tous les utilisateurs naient pas besoin de toutes ses fonctionnalits. Pour rduire le temps de tlchargement, vous pouvez diviser le code de lapplet dans plusieurs fichiers JAR
et ajouter un indice au principal fichier JAR. Le chargeur de classe connat aussi les fichiers JAR qui contiennent un
package ou une ressource particulire. Voir http://java.sun.com/j2se/5.0/docs/guide/jar/jar.html#JAR%20Index
pour en savoir plus sur la procdure dindice.

ASTUCE
Le JDK 5.0 prend en charge un nouveau schma de compression, intitul "pack200", spcifiquement conu pour
compresser les fichiers de classes plus efficacement que lalgorithme de compression ZIP. Sun se targue dun taux de
compression proche de 90 % avec des fichiers JAR plusieurs mga-octets ne contenant que des fichiers de classe.

Livre Java .book Page 591 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

591

Pour dployer ces fichiers, vous devez configurer le serveur Web, de sorte quil serve les fichiers standard pour les
clients traditionnels et les fichiers compresss pour les nouveaux clients qui indiquent dans la requte HTTP quils
sont capables de le dcompresser (voir http://java.sun.com/j2se/5.0/docs/ guide/deployment/deployment-guide/
pack200.html pour en savoir plus).

INFO
Le plug-in Java place le code dapplet en mmoire (voir http://java.sun.com/j2se/5.0/docs/guide/plugin/ developer_
guide/applet_caching.html pour savoir comment agir sur le cache).

Packaging des applications


Nous quittons maintenant le monde des applets et passons au packaging des applications Java. Lorsque vous livrez une application, vous ne souhaitez gnralement pas dployer tout un ensemble de
fichiers de classe. Comme pour les applets, vous devez packager les fichiers de classe ainsi que
dautres ressources exiges par votre programme dans un fichier JAR. Lorsque le programme est
packag, il peut tre charg par une commande simple ou, si le systme dexploitation est correctement
configur, par un double-clic sur le fichier JAR.

Le manifeste
Vous pouvez packager des programmes dapplication, des composants de programmes (parfois
appels "Java Beans" voir le Chapitre 8 du Volume 2) et des bibliothques de code dans des
fichiers JAR. Par exemple, la bibliothque de runtime du JDK est contenue dans un trs grand fichier
rt.jar.
Un fichier JAR est simplement un fichier ZIP qui contient des classes, dautres fichiers ncessaires
un programme et un fichier manifeste qui dcrit les caractristiques particulires de larchive. Le
fichier manifeste est appel MANIFEST.MF et se trouve dans un sous-rpertoire META-INF du fichier
JAR. Le manifeste minimum lgal est un peu terne :
Manifest-version: 1.0

Des manifestes complexes peuvent possder davantage de donnes. Celles-ci sont regroupes en
sections. La premire sappelle Main. Elle sapplique la totalit du fichier JAR. Dautres donnes
peuvent spcifier les proprits des entits nommes telles que les fichiers individuels, les packages
ou les URL. Ces donnes doivent commencer par une entre Name. Les sections sont spares par
des lignes blanches. Par exemple :
Manifest-Version: 1.0
lignes dcrivant cette archive
Name: Woozle.class
lignes dcrivant ce fichier
Name: com/mycompany/mypkg
lignes dcrivant ce package

Livre Java .book Page 592 Jeudi, 25. novembre 2004 3:04 15

592

Au cur de Java 2 - Notions fondamentales

Pour diter le manifeste, placez les lignes que vous voulez diter au manifeste dans un fichier texte.
Puis excutez :
jar cfm JARFileName ManifestFileName . . .

Par exemple, pour crer un nouveau fichier JAR avec un manifeste, excutez :
jar cfm MyArchive.jar manifest.mf com/mycompany/mypkg/*.class

Pour ajouter des lments au manifeste dun fichier JAR existant, placez les ajouts dans un fichier
texte et utilisez une commande telle que
jar ufm MyArchive.jar manifest-additions.mf

Voyez ladresse http://java.sun.com/j2se/5.0/docs/guide/jar/jar.html pour en savoir plus sur le


JAR et les formats de fichier manifeste.

Fichiers JAR auto-extractibles


Pour packager une application, runissez tous les fichiers ncessaires votre application dans un
fichier JAR et ajoutez-y un manifeste spcifiant la classe principale de votre programme celle qui
doit tre invoque en premier par le lanceur de programme Java.
Crez un fichier, par exemple mainclass.mf, contenant une ligne telle que
Main-Class: com/mycompany/mypkg/MainAppClass

ATTENTION
La dernire ligne du manifeste doit se terminer par un caractre de nouvelle ligne, faute de quoi le manifeste ne
pourra pas tre lu correctement. Produire un fichier texte contenant simplement la ligne Main-Class sans terminaison est une erreur commune.

Najoutez pas dextension .class au nom de la classe principale. Ensuite, lancez la commande jar:
jar cvfm MyProgram.jar mainclass.mf files to add

Les utilisateurs nont plus qu lancer le programme par


java -jar MyProgram.jar

Dans certaines configurations du systme dexploitation, vous pourrez lancer lapplication en


double-cliquant sur licne du fichier JAR.
m

Sous Windows, linstallateur dexcution Java cre une association de fichier pour lextension
".jar" qui lance le fichier avec la commande javaw -jar ( la diffrence de la commande java,
la commande javaw nouvre pas de fentre shell).

Sous Solaris, le systme dexploitation reconnat le "nombre magique" dun fichier JAR et le
lance avec la commande java -jar.

Sous Mac OS X, le systme dexploitation reconnat lextension de fichier ".jar" et excute le


programme Java lorsque vous double-cliquez sur un fichier JAR. En outre, lutilitaire de package
dapplication MRJAppBuilder vous permet de transformer un fichier JAR en une application
Mac de premier niveau dans laquelle il est possible de cliquer et qui contient des chemins de
classes personnaliss, des icnes, etc. (Pour en savoir plus, voir http://developer.apple.com/qa/
java/java29.html).

Livre Java .book Page 593 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

593

Les ressources
Les classes employes la fois dans les applets et les applications utilisent gnralement des fichiers
de donnes associs tels que :
m

des fichiers dimages et de son ;

des fichiers de texte contenant les messages et les libells des boutons ;

des fichiers de donnes binaires, par exemple pour indiquer la disposition dune carte.

En Java, un fichier associ de ce type est appel une ressource.


INFO
Sous Windows, le terme de "ressource" a une signification plus spcialise. Les ressources Windows consistent galement en images, libells de boutons, etc., mais elles sont attaches au fichier excutable ; ce dernier y accde
travers une interface de programmation standard. Les ressources Java sont stockes en tant que fichiers spars, ne
faisant pas partie des fichiers de classes ; chaque classe a la charge dy accder et dinterprter les donnes de la
ressource.

Supposons, par exemple, une classe AboutPanel affichant le message suivant (voir Figure 10.11) :
Figure 10.11
Afficher une ressource
situe dans un fichier JAR.

Le titre de ce livre ainsi que lanne du copyright changeront certainement avec la prochaine dition
du livre. Pour pouvoir facilement suivre ce changement, nous allons mettre le texte dans un fichier et
non pas le coder en dur dans une chane de la classe dialogue.
O devons-nous dposer un fichier tel que about.txt? Bien sr, il serait pratique de le placer au
mme endroit que les autres programmes, par exemple dans un fichier JAR.
Le chargeur de classes sait comment parcourir chacun des emplacements possibles jusqu retrouver
le fichier de classes. Toutefois, dans notre cas, nous devons rpter le processus de recherche
manuellement pour localiser le fichier de ressources associ. La fonctionnalit de chargement de
ressource automatise cette tche. On suivra les tapes suivantes pour charger une ressource :
1. Charger lobjet Class pour la classe possdant une ressource, par exemple AboutPanel.class.

Livre Java .book Page 594 Jeudi, 25. novembre 2004 3:04 15

594

Au cur de Java 2 - Notions fondamentales

2. Appeler getResource(name) pour obtenir le fichier de ressources en tant quURL.


3. Si la ressource est un fichier image ou audio, la lire directement laide de la mthode getImage
ou getAudioClip.
4. Dans les autres cas, utiliser la mthode openStream sur lURL pour lire les donnes du fichier
(voir Chapitre 12 pour plus de dtails sur les flux).
Le chargeur de classes mmorise lemplacement o il a charg la classe ; il peut alors rechercher
dans cet emplacement la ressource associe.
Par exemple, vous pouvez utiliser les instructions suivantes pour crer une icne partir du fichier
image about.gif en suivant cette procdure :
URL url = AboutPanel.class.getResource("about.gif");
ImageIcon icon = new ImageIcon(url);

Cela revient tablir le fichier about.gif au mme endroit que celui o vous avez trouv AboutPanel.class.
Par exemple, vous pouvez utiliser les instructions suivantes pour lire le fichier about.txt:
URL url = AboutPanel.class.getResource("about.txt");
InputStream stream = url.openStream();

Cette suite dinstructions peut tre abrge grce la mthode getResourceAsStream qui renvoie
un InputStream au lieu dune URL :
InputStream stream
= AboutPanel.class.getResourceAsStream("about.txt");

Pour lire partir de ce flux, vous devez savoir comment traiter lentre (voir Chapitre 12). Dans le
programme exemple, nous lisons le flux ligne par ligne, comme ceci :
InputStream stream = AboutPanel.class.
getResourceAsStream("about.txt");
Scanner in = Scanner.create(stream);
while (in.hasNext())
textArea.append(in.nextLine+"\n");

Les fichiers dexemple de cet ouvrage incluent un fichier JAR nomm ResourceText.jar qui
contient tous les fichiers de classe pour cet exemple ainsi que le fichier de ressource about.text et
about.gif. Nous voyons que le programme trouve le fichier de ressource au mme endroit que le
fichier de classes, cest--dire dans le fichier JAR. LExemple 10.12 prsente le source.
ASTUCE
Comme nous lavons vu dans ce chapitre, les fichiers images et audio peuvent tre placs dans un fichier JAR. On peut
y accder simplement laide des mthodes getImage et getAudioClip qui effectuent une recherche automatique de fichier JAR. En revanche, pour charger dautres fichiers partir dun fichier JAR, il faudra utiliser la mthode
getResourceAsStream.

Il est possible de placer un fichier de ressource dans un sous-rpertoire plutt que dans le rpertoire
du fichier de classes. Vous pouvez hirarchiser les noms des ressources :
data/text/about.txt

Livre Java .book Page 595 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

595

Ce nom de ressource relatif est interprt partir du package de la classe chargeant la ressource.
Remarquez lutilisation obligatoire du sparateur /, quel que soit le sparateur de rpertoire du
systme sur lequel se trouvent finalement les fichiers de ressources. Ainsi, sous Windows, le chargeur de
ressources convertit automatiquement / en sparateur \.
Un nom de ressource commenant par un / est appel un nom de ressource absolu. Il est localis par
Java de la mme faon quune classe situe dans un package. Par exemple, la ressource
/corejava/title.txt

est situe dans le rpertoire corejava (qui peut tre soit un sous-rpertoire dans le chemin des classes,
soit dans un fichier JAR soit, pour les applets, sur le serveur Web).
Le dispositif de chargement de ressources se limite lautomatisation du chargement des
fichiers. Il nexiste pas de mthode standard pour interprter le contenu dun fichier de ressources.
Chaque programme doit interprter sa faon le contenu de ses fichiers de ressources.
Une autre application courante des ressources est linternationalisation des programmes. Les chanes
dpendant dune langue, comme les messages et les libells de linterface utilisateur, sont stockes
dans des fichiers ressource, avec un fichier par langue. LAPI Internationalization, prsente au
Chapitre 10 de Au cur de Java 2 Volume 2 (ditions CampusPress, 2003), supporte une mthode
standard dorganisation et daccs ces fichiers de localisation.
Exemple 10.12 : ResourceTest.java
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.applio.*;
java.net.*;
java.util.*;
javax.swing.*;

public class ResourceText


{
public static void main(String[] args)
{
ResourceTestFrame frame = new ResourceTestFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau disposant de composants pour dmontrer
laccs aux ressources partir dun fichier JAR.
*/
class ResourceTestFrame extends JFrame
{
public ResourceTestFrame()
{
setTitle("ResourceTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
add(new AboutPanel());
}

Livre Java .book Page 596 Jeudi, 25. novembre 2004 3:04 15

596

Au cur de Java 2 - Notions fondamentales

public static final int DEFAULT_WIDTH = 300;


public static final int DEFAULT_HEIGHT = 300;
}
/**
Un panneau avec une zone de texte et un bouton "About". Appuyer
dessus remplit la zone avec le texte dune ressource.
*/
class AboutPanel extends JPanel
{
public AboutPanel()
{
setLayout(new BorderLayout());
// Ajouter la zone de texte
textArea = new JTextArea();
add(new JScrollPane(textArea), BorderLayout.CENTER);
// Ajouter le bouton About
URL aboutURL = AboutPanel.class.getResource("about.gif");
JButton aboutButton = new JButton("About",
new ImageIcon(aboutURL));
aboutButton.addActionListener(new AboutAction());
add(aboutButton, BorderLayout.SOUTH);
}
private JTextArea textArea;
private class AboutAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
InputStream stream =
AboutPanel.class.getResourceAsStream("about.txt");
Scanner in = new Scanner(stream);
while (in.hasNext())
textArea.append(in.nextLine() + "\n");
}
}
}
java.lang.Class 1.0

URL getResource(String name) 1.1


InputStream getResourceAsStream(String name) 1.1

Recherche la ressource dans le mme emplacement que la classe et renvoie une URL ou un flux
dentre pour charger la ressource. La mthode renvoie null si la ressource nest pas trouve, et
par consquent ne lance pas dexception derreur dE/S.
Paramtres :

name

le nom de la ressource.

Livre Java .book Page 597 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

597

Verrouillage
Nous avons prcis au Chapitre 4 que vous pouvez verrouiller (seal) un package en langage Java
pour empcher dautres classes Java de sinstaller. Un package doit tre verrouill si vous utilisez
des classes, des mthodes et des champs visibles pour le package dans votre code. Sans cela, dautres
classes peuvent se placer dans le mme package et ainsi obtenir un accs aux fonctionnalits qui lui sont
visibles.
Par exemple, si vous verrouillez le package com.mycompany.util, aucune classe extrieure ne peut
tre dfinie par linstruction
package com.mycompany.util;

Pour ce faire, dposez toutes les classes du package dans un fichier JAR. Par dfaut, les packages
dun fichier JAR ne sont pas verrouills. Vous pouvez modifier cela en crivant la ligne
Sealed: true

dans la section principale du manifeste. Pour chaque package, vous pouvez spcifier si vous dsirez
quil soit verrouill ou non, en ajoutant une section supplmentaire au manifeste du fichier JAR :
Name: com/mycompany/util/
Sealed: true
Name: com/mycompany/misc/
Sealed: false

Pour verrouiller un package, crez un fichier texte avec les instructions du fichier texte. Puis lancez
la commande jar de la manire habituelle :
jar cvfm MyArchive.jar manifest.mf fichiers ajouter

Java Web Start


Java Web Start est une nouvelle technologie destine amliorer lexprience de lutilisateur avec
les programmes Java dlivrs sur Internet. Voici les principales diffrences entre les applications
Java Web Start et les applets.
m

Java Web Start permet de dlivrer des applications Java ordinaires, qui sont dmarres par un
appel ing la mthode main dune classe. Il nest pas ncessaire dhriter de Applet.

Une application Java Web Start ne demeure pas dans un navigateur. Elle saffiche en dehors de lui.

Une application Java Web Start peut tre lance depuis le navigateur, mais le mcanisme
sous-jacent est assez diffrent de celui dun applet. Les navigateurs sont bien intgrs dans
un environnement dexcution Java qui excute les applets. Lintgration de Java Web Start
est moins fine. Le navigateur lance simplement une application externe lorsquil charge un
descripteur dapplication Java Web Start. Le mcanisme ressemble celui utilis pour
lancer dautres applications daide comme Adobe Acrobat ou RealAudio. Les fournisseurs
de navigateurs, qui se montrent plutt rticents, ne pourront pas interfrer avec ce mcanisme.

Lorsquune application Java Web Start a t tlcharge, elle peut tre dmarre en dehors du
navigateur.

Livre Java .book Page 598 Jeudi, 25. novembre 2004 3:04 15

598

Au cur de Java 2 - Notions fondamentales

Java Web Start possde une "bote sable" plutt tranquille qui accorde un accs aux ressources
locales pour les applications non signes.

Pour prparer une application en vue dune livraison par Java Web Start, vous devez la packager
dans un ou plusieurs fichiers JAR. Prparez ensuite un fichier de description au format Java Network
Launch Protocol (JNLP). Aprs cela, placez-les sur un serveur Web. Vous devez vous assurer que
votre serveur Web rapporte un type MIME application/x-java-jnlp-file pour les fichiers
ayant une extension .jnlp (les navigateurs utilisent le type MIME pour dterminer lapplication
daide lancer). Consultez la documentation de votre serveur Web pour en savoir plus.
ASTUCE
Pour tester Java Web Start, installez Tomcat depuis ladresse jakarta.apache.org/tomcat.
Tomcat est un conteneur destin aux servlets et aux pages de serveurs JSP Java, mais il sert aussi les pages Web. La
version actuelle est prconfigure pour grer les fichiers JNLP.

Testons Java Web Start pour dlivrer lapplication de calculette du Chapitre 9. Procdez comme
suit :
1. Compilez Calculator.java.
2. Prparez un fichier de manifeste Calculator.mf avec la ligne
Main-Class: Calculator

3. Produisez un fichier JAR avec la commande


jar cvfm Calculator.jar Calculator.mf *.class

4. Prparez le fichier de lancement Calculator.jnlp avec le contenu suivant :


<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+"codebase="http://localhost:8080/calculator/"
href="Calculator.jnlp">
<information>
<title>Calculator Demo Application</title>
<vendor>Cay S. Horstmann</vendor>
<description>A Calculator</description>
<offline-allowed/>
</information>
<resources>
<j2se version="5.0+"/>
<jar href="Calculator.jar"/>
</resources>
<application-desc/>
</jnlp>

Le format du fichier de lancement est assez explicatif. Pour obtenir une spcification complte,
voyez http://java.sun.com/products/javawebstart/docs/developersguide.html.
5. Si vous utilisez Tomcat, crez un rpertoire tomcat/webapps/calculator, o tomcat correspond au rpertoire de base de votre installation Tomcat. Crez un sous-rpertoire tomcat/

Livre Java .book Page 599 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

599

webapps/calculator/WEB-INF, et placez le fichier Web.xml minimal suivant dans le sousrpertoire WEB-INF:


<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/Web-app_2_3.dtd">
<Web-app>
</Web-app>

6. Placez le fichier JAR et le fichier de lancement sur votre serveur Web, de sorte que lURL
concorde avec lentre de codebase du fichier JNLP. Si vous utilisez Tomcat, placez-les dans le
rpertoire tomcat/webapps/calculator.
7. Vrifiez que votre navigateur a t configur pour Java Web Start, en vous assurant que le type
MIME application/x-java-jnlp-file est associ lapplication javaws. Si vous avez
install le JDK, la configuration devrait se faire automatiquement.
8. Lancez Tomcat.
9. Pointez votre navigateur vers le fichier JNLP. Si, par exemple, vous utilisez Tomcat, accdez
http://localhost:8080/calculator/Calculator.jnlp.
10. La fentre de lancement de Java Web Start saffiche (voir Figure 10.12). Peu aprs, la calculatrice
saffiche, avec une bordure indiquant une application Java Web Start (voir Figure 10.13).
Figure 10.12
Lancement de Java Web
Start.

Figure 10.13
La calculatrice fournie
par Java Web Start.

11. Lorsque vous accdez nouveau au fichier JNLP, lapplication est rcupre depuis la mmoire
tampon. Vous pouvez en consulter le contenu laide du panneau de contrle du plug-in Java
(voir Figure 10.14). Depuis le JDK 5.0, ce panneau de contrle sert la fois pour les applets et
les applications Java Web Start. Sous Windows, recherchez ce panneau de contrle dans le
Panneau de configuration. Sous Linux, excutez jdk/jre/bin/ControlPanel.

Livre Java .book Page 600 Jeudi, 25. novembre 2004 3:04 15

600

Au cur de Java 2 - Notions fondamentales

Figure 10.14
Le cache de lapplication

LAPI JNLP
Pour permettre une application Java Web Start dobtenir un accs intgral la machine locale,
lapplication doit tre signe numriquement (voir Chapitre 9 du Volume 2 pour en savoir plus).
Comme avec les applets, une application Java Web Start non signe et tlcharge sur Internet
prsente invitablement des risques et excute une bote sable, avec un accs minimal lordinateur local. Toutefois, avec des privilges de scurit minimaux, lAPI JNLP offre aux dveloppeurs
dapplications certains outils pour accder aux ressources locales.
Il existe par exemple des services pour charger et enregistrer des fichiers, qui sont toutefois assez
restreints. Lapplication ne peut pas tudier le systme de fichiers ni spcifier les noms des fichiers.
Une bote de dialogue saffiche, et cest lutilisateur quil revient de choisir le fichier. Avant que la
bote ne saffiche, lutilisateur reoit un avertissement quil doit accepter pour continuer (voir
Figure 10.15). En outre, lAPI ne permet pas rellement au programme daccder un objet File.
Plus prcisment, lapplication na aucun moyen de retrouver lemplacement du fichier. Les
programmeurs reoivent donc les outils pour implmenter les actions standard "Fichier/Ouvrir" et
"Fichier/Enregistrer". Le plus dinformations systme possibles sont masques aux applications
douteuses.
Figure 10.15
Un avertissement de scurit
Java Web Start.

Livre Java .book Page 601 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

601

LAPI fournit les services suivants :


m

chargement et enregistrement de fichiers ;

accs au presse-papiers ;

tlchargement dun fichier ;

impression ;

stockage et rcupration dinformations de configuration persistantes ;

affichage dun document dans le navigateur par dfaut ;

vrification du fait quune seule instance dune application sexcute (nouveaut du JDK 5.0).

Pour accder un service, utilisez ServiceManager, comme ceci :


FileSaveService service = (FileSaveService)
ServiceManager.lookup("javax.jnlp.FileSaveService");

Cet appel dclenche une exception UnavailableServiceException si le service nest pas


disponible.
INFO
Pour compiler les programmes qui utilisent lAPI JNLP, le fichier javaws.jar doit tre inclus dans le chemin de
classe. Il se trouve dans le sous-rpertoire jre/lib du JDK.

Nous allons voir maintenant les services JNLP les plus utiles. Pour enregistrer un fichier, vous
pouvez fournir des suggestions de noms, tant pour le chemin initial que pour les extensions de
fichiers dans la bote de dialogue Fichier, ainsi que pour les donnes enregistrer pour le nom de fichier.
Par exemple,
service.saveFileDialog(".", new String[] { "txt" }, data, "calc.txt");

Les donnes doivent tre dlivres dans un InputStream. Ceci peut tre dlicat agencer. Le
programme de lExemple 10.13 utilise la stratgie suivante :
1. cration dun ByteArrayOutputStream pour contenir les octets enregistrer ;
2. cration dun PrintStream qui envoie ses donnes au ByteArrayOutputStream;
3. impression des informations enregistrer sur le PrintStream;
4. cration dun ByteArrayInputStream pour lire les octets enregistrs ;
5. transfert du flux la mthode saveFileDialog.
Vous en saurez plus sur les flux au Chapitre 12. Pour lheure, vous pouvez tudier les dtails du
programme dexemple.

Livre Java .book Page 602 Jeudi, 25. novembre 2004 3:04 15

602

Au cur de Java 2 - Notions fondamentales

Pour lire des donnes dun fichier, utilisez plutt FileOpenService. Son openFileDialog
reoit des suggestions pour le nom de chemin initial et les extensions de fichier de la bote de
dialogue Fichier ; il renvoie ensuite un objet FileContents. Vous pouvez alors appeler la
mthode getInputStream pour lire les donnes du fichier. Si lutilisateur na pas choisi un
fichier, la mthode openFileDialog renvoie null:
FileOpenService service = (FileOpenService)
ServiceManager.lookup("javax.jnlp.FileOpenService");
FileContents contents =
service.openFileDialog(".", new String[] { "txt" });
if (contents!= null)
{
InputStream in = contents.getInputStream();
. . .
}

Pour afficher un document dans le navigateur par dfaut (comme avec la mthode showDocument
dun applet), utilisez linterface BasicService. Sachez que certains systmes (en particulier de
nombreux systmes UNIX et Linux) peuvent ne pas avoir de navigateur par dfaut :
BasicService service = (BasicService)
ServiceManager.lookup("javax.jnlp.BasicService");
if (service.isWebBrowserSupported())
service.showDocument(url);
else . . .

Il existe un service rudimentaire, appel PersistenceService, qui permet une application de


stocker de petites quantits dinformations de configuration et de les rcuprer lorsque lapplication
sexcute nouveau. Ce service est ncessaire en ce sens quune application douteuse ne pourra pas
indiquer demplacement pour un fichier de configuration.
Le mcanisme est identique aux cookies HTTP. La banque persistante utilise les URL la manire
de cls. Les URL nont pas besoin de pointer vers une vritable ressource Web. Le service sen sert
simplement comme dun schma de dnomination hirarchique commode. Pour toute cl dURL
donne, une application peut stocker des donnes binaires arbitraires (attention, toutefois, la banque
peut restreindre la taille du bloc de donnes).
Pour isoler les applications les unes des autres, une application donne peut nutiliser que les cls
dURL commenant par son codebase (comme indiqu dans le fichier JNLP). Si vous tlchargez
par exemple une application ladresse http://myserver.com/apps, elle ne pourra utiliser que les
cls du formulaire http://myserver.com/apps/subkey1/subkey2/... Les tentatives daccs dautres
cls choueront.
Une application peut appeler la mthode getCodeBase du BasicService pour retrouver son codebase.
Pour crer une nouvelle cl, faites appel la mthode create de PersistenceService:
URL url = new URL(codeBase, "mykey");
service.create(url, maxSize);

Pour accder aux informations associes une cl particulire, appelez la mthode get. Elle renvoie
un objet FileContents par lequel vous pouvez lire et crire les donnes de cl. Par exemple,
FileContents contents = service.get(url);
InputStream in = contents.getInputStream();
OutputStream out = contents.getOutputStream(true); // true = crasement

Livre Java .book Page 603 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

603

Malheureusement, il nexiste pas de mthode commode pour dcouvrir si une cl existe dj ou si


vous devez la crer. Vous pouvez esprer que la cl existe et appeler get. Si lappel dclenche une
exception FileNotFoundException, vous devez crer la cl.
INFO
Depuis le JDK 5.0, les applications Java Web Start et les applets peuvent imprimer grce lAPI dimpression normale.
Un avertissement de scurit apparat, demandant la permission lutilisateur pour accder limprimante. Pour en
savoir plus sur lAPI dimpression, passez au chapitre sur AWT au Volume 2.

Le programme de lExemple 10.13 constitue une simple amlioration de lapplication de la


calculette. Elle possde maintenant une bande papier virtuelle qui effectue le suivi de tous les
calculs. Il est ainsi possible denregistrer et de charger lhistorique des calculs. Pour mettre la
banque persistante en action, lapplication vous permet de dfinir le titre du cadre. Si vous
excutez nouveau lapplication, elle rcupre votre choix de titre dans la banque persistante
(voir Figure 10.16).
Figure 10.16
Lapplication WebStartCalculator.

Exemple 10.13 : WebStartCalculator.java


import
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.io.*;
java.net.*;
javax.swing.*;
javax.swing.text.*;
javax.jnlp.*;

/**
Une calculette avec lhistorique des calculs qui peut
tre dploy sous forme dapplication Java Web Start.

Livre Java .book Page 604 Jeudi, 25. novembre 2004 3:04 15

604

Au cur de Java 2 - Notions fondamentales

*/
public class WebStartCalculator
{
public static void main(String[] args)
{
CalculatorFrame frame = new CalculatorFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau de calculatrice et un menu pour charger et
enregistrer lhistorique des calculs.
*/
class CalculatorFrame extends JFrame
{
public CalculatorFrame()
{
setTitle();
panel = new CalculatorPanel();
add(panel);
JMenu fileMenu = new JMenu("File");
JMenuItem openItem = fileMenu.add("Open");
openItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
open();
}
});
JMenuItem saveItem = fileMenu.add("Save");
saveItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
save();
}
});
JMenuBar menuBar = new JMenuBar();
menuBar.add(fileMenu);
setJMenuBar(menuBar);
pack();
}
/**
Rcupre le titre de la banque persistante ou
demande le titre lutilisateur sil nexiste pas
dentre pralable.
*/
public void setTitle()
{
try

Livre Java .book Page 605 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

{
String title = null;
BasicService basic = (BasicService)
ServiceManager.lookup("javax.jnlp.BasicService");
URL codeBase = basic.getCodeBase();
PersistenceService service = (PersistenceService)
ServiceManager.lookup("javax.jnlp.PersistenceService");
URL key = new URL(codeBase, "title");
try
{
FileContents contents = service.get(key);
InputStream in = contents.getInputStream();
BufferedReader reader =
new BufferedReader(new InputStreamReader(in));
title = reader.readLine();
}
catch (FileNotFoundException e)
{
title = JOptionPane.showInputDialog
("Please supply a frame title:");
if (title == null) return;
service.create(key, 100);
FileContents contents = service.get(key);
OutputStream out = contents.getOutputStream(true);
PrintStream printOut = new PrintStream(out);
printOut.print(title);
}
setTitle(title);
}
catch (UnavailableServiceException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (MalformedURLException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (IOException e)
{
JOptionPane.showMessageDialog(this, e);
}
}
/**
Ouvre un fichier dhistorique et actualise laffichage.
*/
public void open()
{
try
{
FileOpenService service = (FileOpenService)
ServiceManager.lookup("javax.jnlp.FileOpenService");
FileContents contents = service.openFileDialog
(".", new String[] { "txt" });

605

Livre Java .book Page 606 Jeudi, 25. novembre 2004 3:04 15

606

Au cur de Java 2 - Notions fondamentales

JOptionPane.showMessageDialog(this, contents.getName());
if (contents!= null)
{
InputStream in = contents.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
String line;
while ((line = reader.readLine())!= null)
{
panel.append(line);
panel.append("\n");
}
}
}
catch (UnavailableServiceException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (IOException e)
{
JOptionPane.showMessageDialog(this, e);
}
}
/**
Enregistre lhistorique des calculs dans un fichier.
*/
public void save()
{
try
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream printOut = new PrintStream(out);
printOut.print(panel.getText());
InputStream data = new ByteArrayInputStream(out.toByteArray());
FileSaveService service = (FileSaveService)
ServiceManager.lookup("javax.jnlp.FileSaveService");
service.saveFileDialog(".", new String[]
{ "txt" }, data, "calc.txt");
}
catch (UnavailableServiceException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (IOException e)
{
JOptionPane.showMessageDialog(this, e);
}
}
private CalculatorPanel panel;
}

/**
Un panneau avec les boutons de la calculette et laffichage
des rsultats.

Livre Java .book Page 607 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

*/
class CalculatorPanel extends JPanel
{
/**
Affiche le panneau.
*/
public CalculatorPanel()
{
setLayout(new BorderLayout());
result = 0;
lastCommand = "=";
start = true;
// ajouter laffichage
display = new JTextArea(10, 20);
add(new JScrollPane(display), BorderLayout.NORTH);
ActionListener insert = new InsertAction();
ActionListener command = new CommandAction();
// ajouter les boutons sur une grille 4 x 4
panel = new JPanel();
panel.setLayout(new GridLayout(4, 4));
addButton("7",
addButton("8",
addButton("9",
addButton("/",

insert);
insert);
insert);
command);

addButton("4",
addButton("5",
addButton("6",
addButton("*",

insert);
insert);
insert);
command);

addButton("1",
addButton("2",
addButton("3",
addButton("-",

insert);
insert);
insert);
command);

addButton("0",
addButton(".",
addButton("=",
addButton("+",

insert);
insert);
command);
command);

add(panel, BorderLayout.CENTER);
}
/**
Rcupre le texte de lhistorique.
@return Lhistorique des calculs
*/
public String getText()
{
return display.getText();
}

607

Livre Java .book Page 608 Jeudi, 25. novembre 2004 3:04 15

608

Au cur de Java 2 - Notions fondamentales

/**
Annexe une chane au texte de lhistorique.
@param s La chane annexer
*/
public void append(String s)
{
display.append(s);
}
/**
Ajoute un bouton au panneau central.
@param label Lintitul du bouton
@param listener Lcouteur du bouton
*/
private void addButton(String label, ActionListener listener)
{
JButton button = new JButton(label);
button.addActionListener(listener);
panel.add(button);
}
/**
Cette action insre la chane daction du bouton la
fin du texte affich.
*/
private class InsertAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String input = event.getActionCommand();
start = false;
display.append(input);
}
}
/**
Cette action excute la commande traduite par
la chane daction du bouton.
*/
private class CommandAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String command = event.getActionCommand();
if (start)
{
if (command.equals("-"))
{
display.append(command);
start = false;
}
else
lastCommand = command;
}
else
{
try
{
int lines = display.getLineCount();

Livre Java .book Page 609 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

int lineStart = display.getLineStartOffset(lines - 1);


int lineEnd = display.getLineEndOffset(lines - 1);
String value = display.getText(
lineStart, lineEnd - lineStart);
display.append(" ");
display.append(command);
calculate(Double.parseDouble(value));
if (command.equals("="))
display.append("\n" + result);
lastCommand = command;
display.append("\n");
start = true;
}
catch (BadLocationException e)
{
e.printStackTrace();
}
}
}
}
/**
Ralise le calcul en attente.
@param x La valeur accumuler avec le rsultat prcdent.
*/
public void calculate(double x)
{
if (lastCommand.equals("+")) result += x;
else if (lastCommand.equals("-")) result -= x;
else if (lastCommand.equals("*")) result *= x;
else if (lastCommand.equals("/")) result /= x;
else if (lastCommand.equals("=")) result = x;
}
private
private
private
private
private

JTextArea display;
JPanel panel;
double result;
String lastCommand;
boolean start;

}
javax.jnlp.ServiceManager
static String[] getServiceNames()

Renvoie les noms de tous les services disponibles.

static Object lookup(String name)

Renvoie un service avec un nom donn.


javax.jnlp.BasicService
URL getCodeBase()

Renvoie le codebase de cette application.

boolean isWebBrowserSupported()

Renvoie true si lenvironnement Web Start peut lancer un navigateur Web.

boolean showDocument(URL url)

Tente dafficher lURL donne dans un navigateur. Renvoie true si la requte a russi.

609

Livre Java .book Page 610 Jeudi, 25. novembre 2004 3:04 15

610

Au cur de Java 2 - Notions fondamentales

javax.jnlp.FileContents

InputStream getInputStream()

Renvoie un flux dentre pour lire le contenu du fichier.

OutputStream getOutputStream(boolean overwrite)

Renvoie un flux de sortie pour crire dans le fichier. Si overwrite vaut true, le contenu du
fichier est remplac.

String getName()

Renvoie le nom du fichier (mais pas le chemin complet du rpertoire).

boolean canRead()
boolean canWrite()

Ces mthodes renvoient true si le fichier sous-jacent est lisible ou que lon peut crire dedans.
javax.jnlp.FileOpenService

FileContents openFileDialog(String pathHint, String[] extensions)


FileContents[] openMultiFileDialog(String pathHint, String[] extensions)

Ces mthodes affichent un avertissement utilisateur et un slecteur de fichiers. Elles renvoient


des descripteurs de contenu du ou des fichiers choisis par lutilisateur ou null si lutilisateur na
pas choisi de fichier.
javax.jnlp.FileSaveService

FileContents saveFileDialog(String pathHint, String[] extensions, InputStream


data, String nameHint)

FileContents saveAsFileDialog(String pathHint, String[] extensions, FileContent s


data)

Ces mthodes affichent un avertissement utilisateur et un slecteur de fichiers. Elles crivent les
donnes et renvoient des descripteurs de contenu du ou des fichiers choisis par lutilisateur ou
null si lutilisateur na pas choisi de fichier.
javax.jnlp.PersistenceService

long create(URL key, long maxsize)

Cre une entre dans une banque persistante pour la cl donne. Renvoie la taille maximale
accorde par la banque persistante.

void delete(URL key)

Supprime lentre pour la cl donne.

String[] getNames(URL url)

Renvoie les noms relatifs de toutes les cls qui commencent par lURL donne.

FileContents get(URL key)

Rcupre un descripteur de contenu grce auquel vous pouvez modifier les donnes associes
la cl donne. Sil nexiste pas dentre pour la cl, une exception FileNotFoundException est
dclenche.

Livre Java .book Page 611 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

611

Stockage des prfrences dapplications


La plupart des programmes peuvent tre configurs par leurs utilisateurs. Ils doivent ensuite enregistrer les prfrences dfinies, puis les restaurer au moment du redmarrage. Vous avez dj vu
comment une application Java Web Start peut utiliser une banque persistante dans ce but. Or le JDK
1.4 a introduit un mcanisme diffrent et plus puissant pour les applications locales. Nous dcrirons
ce mcanisme ici, mais nous nous intresserons une approche plus simple permettant de stocker les
informations de configuration que les applications Java adoptent gnralement.

Concordances de proprits
Une concordance de proprits est une structure de donnes qui stocke les paires cl/valeur. Les
concordances de proprits permettent souvent de stocker des informations de configuration.
Elles affichent trois caractristiques particulires :
m

Les cls et les valeurs sont des chanes.

Le jeu peut facilement tre enregistr dans un fichier et charg partir dun fichier.

Il existe un tableau secondaire pour les valeurs par dfaut.

La classe de la plate-forme Java qui implmente une concordance de proprit est appele Properties.
Les concordances de proprits sont utiles pour spcifier des options de configuration pour les
programmes. Par exemple :
Properties settings = new Properties();
settings.put("font", "Courier");
settings.put("size", "10");
settings.put("message", "Hello, World");

Utilisez la mthode store pour enregistrer cette liste de proprits dans un fichier. Ici, nous enregistrons simplement la concordance de proprits dans le fichier Myprog.properties. Le deuxime
argument est un commentaire inclus dans le fichier :
FileOutputStream out = new FileOutputStream("Myprog.properties");
settings.store(out, "Myprog Properties");

Le jeu dexemple donne la sortie suivante :


#Myprog Properties
#Tue Jun 15 07:22:52
font=Courier
size=10
message=Hello, World

2004

Pour charger les proprits dun fichier, utilisez


FileInputStream in = new FileInputStream("Myprog.properties");
settings.load(in);

Nous appliquerons cette technique plus loin pour que vos utilisateurs puissent personnaliser le
programme NotHelloWorld selon leur got. Nous leur permettrons de spcifier les lments
suivants dans le fichier de configuration CustomWorld.properties:
m

la taille de la fentre ;

la police ;

Livre Java .book Page 612 Jeudi, 25. novembre 2004 3:04 15

612

Au cur de Java 2 - Notions fondamentales

le corps ;

la couleur du texte ;

la chane du message.

Si lutilisateur en omet certains, nous fournirons des paramtres par dfaut.


La classe Properties possde deux mcanismes pour fournir les paramtres par dfaut. Tout
dabord, ds que vous tudiez la valeur dune chane, vous pouvez spcifier un paramtre par dfaut
utiliser lorsque la cl nest pas prsente :
String font = settings.getProperty("font", "Courier");

Sil existe une proprit "font" dans le tableau des proprits, la police utilisera cette chane. Dans
le cas contraire, la police "Courier" sera utilise par dfaut.
Sil vous parat trop ennuyeux de spcifier le paramtre par dfaut pour chaque appel getProperty, vous pouvez packager tous les paramtres par dfaut dans une concordance de proprit
secondaire et fournir cette concordance dans le constructeur de votre tableau de recherche :
Properties defaultSettings = new Properties();
defaultSettings.put("font", "Courier");
defaultSettings.put("size", "10");
defaultSettings.put("color.red", "0");
. . .
Properties settings = new Properties(defaultSettings);
FileInputStream in = new FileInputStream("CustomWorld.properties");
settings.load(in);
. . .

Vous pouvez en effet spcifier les paramtres par dfaut si vous attribuez un autre paramtre de
concordance de proprits au constructeur defaultSettings, mais ce nest pas une opration trs
orthodoxe.
LExemple 10.14 est le programme personnalisable "Hello, Custom World". Modifiez simplement
le fichier .properties pour adapter laffichage du programme vos gots (voir Figure 10.17).
Figure 10.17
Le programme
personnalis Hello,
World.

INFO
Les proprits sont des tableaux simples naffichant aucune structure hirarchique. Lintroduction dune fausse
hirarchie avec des noms de cls comme window.main.color, window.main.title, etc. est assez frquente.
Toutefois, la classe Properties ne possde pas de mthode qui permette dorganiser une telle hirarchie. Si vous
stockez des informations de configuration complexes, prfrez la classe Preferences (voir la section suivante).

Livre Java .book Page 613 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

613

INFO
La classe Properties tend la classe Hashtable. Toutes les mthodes Hashtable sont donc disponibles pour les
objets Properties. Certaines mthodes sont utiles. size, par exemple, renvoie le nombre de proprits possibles
(la faon nest pas trs lgante, car vous ne connaissez pas le nombre des paramtres par dfaut). De mme, keys
renvoie une numration de toutes les cls, lexception des paramtres par dfaut. Une deuxime mthode, appele propertyNames, renvoie toutes les cls. Quant la mthode put, elle est assez dangereuse, car elle ne sassure
pas que vous placiez des chanes dans le tableau.
La rgle dhritage "est" sapplique-t-elle ici ? Chaque concordance de proprits possde-t-elle une table de
hachage ? Pas vraiment. Quelles soient vraies nest quun dtail de limplmentation. Il vaut peut-tre mieux considrer quune concordance de proprits dispose dune table de hachage. Mais alors, la table de hachage devrait tre
une variable dinstance prive. En fait, dans ce cas, la concordance de proprits utilise deux tables de hachage : lune
pour les paramtres par dfaut et lautre pour les valeurs qui ne sont pas des paramtres par dfaut.
Pour amliorer la conception, on pourrait avoir :
class Properties
{
public String getProperty(String) { . . . }
public void put(String, String) { . . . }
. . .
private Hashtable nonDefaults;
private Hashtable defaults;
}

Ne croyez pas pour autant que nous vous dconseillions la classe Properties de la bibliothque Java. Si vous
prenez garde de ny mettre rien dautre que des chanes, elle fonctionne bien. Mais rflchissez avant dutiliser un
hritage "rapide et sale" dans vos programmes.

Exemple 10.14 : CustomWorld.java


import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.util.*;
java.io.*;
javax.swing.*;

/**
Ce programme montre comment personnaliser un programme "Hello, World"
avec un fichier de proprits.
*/
public class CustomWorld
{
public static void main(String[] args)
{
CustomWorldFrame frame = new CustomWorldFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Ce cadre affiche un message. La taille du cadre, le texte du message,
la police et la couleur sont dfinis dans un fichier de proprits.
*/
class CustomWorldFrame extends JFrame
{
public CustomWorldFrame()

Livre Java .book Page 614 Jeudi, 25. novembre 2004 3:04 15

614

Au cur de Java 2 - Notions fondamentales

{
Properties defaultSettings = new Properties();
defaultSettings.put("font","Monospaced");
defaultSettings.put("width", "300");
defaultSettings.put("height", "200");
defaultSettings.put("messag e", "Hello, World");
defaultSettings.put("color.red" , "0 50 50");
defaultSettings.put("color.green", "50");
defaultSettings.put("color.blue", "50");
defaultSettings.put("ptsize", "12");
Properties settings = new Properties(defaultSettings);
try
{
FileInputStream in =
new FileInputStream("CustomWorld.properties");
settings.load(in);
}
catch (IOException e)
{
e.printStackTrace();
}
int red = Integer.parseInt(settings.getProperty("color.red"));
int green = Integer.parseInt(settings.getProperty("color.green"));
int blue = Integer.parseInt(settings.getProperty("color.blue"));
Color foreground = new Color(red, green, blue);
String name = settings.getProperty("font");
int ptsize = Integer.parseInt(settings.getProperty("ptsize"));
Font f = new Font(name, Font.BOLD, ptsize);
int hsize = Integer.parseInt(settings.getProperty("width"));
int vsize = Integer.parseInt(settings.getProperty("height"));
setSize(hsize, vsize);
setTitle(settings.getProperty("message"));
JLabel label = new JLabel(
settings.getProperty("message"), SwingConstants.CENTER);
label.setFont(f);
label.setForeground(foreground);
add(label);
}
}
java.util.Properties 1.0

Properties()

Cre une liste vide de proprits.

Properties(Properties defaults)

Cre une liste vide de proprits avec un jeu de paramtres par dfaut.
Paramtres :

defaults

Les paramtres par dfaut utiliser pour les recherches.

Livre Java .book Page 615 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

615

String getProperty(String key)

Rcupre une concordance de proprits. Renvoie la chane associe la cl, la chane associe
la cl dans le tableau des paramtres par dfaut si elle tait absente du tableau ou null si la cl
tait absente du tableau des paramtres par dfaut.
Paramtres :

key

La cl dont il faut rcuprer la chane associe.

String getProperty(String key, String defaultValue)

Rcupre une proprit avec une valeur par dfaut si la cl nest pas trouve. Renvoie la chane
associe la cl ou la chane par dfaut si elle tait absente du tableau.
Paramtres :

key

La cl dont il faut rcuprer la chane associe.

defaultValue

La chane renvoyer si la cl nest pas prsente.

void load(InputStream in) throws IOException

Charge une concordance de proprits partir dun flux dentre.


Paramtres :

in

Le flux dentre.

void store(OutputStream out, String header) 1.2

Enregistre une concordance de proprits dans un flux de sortie.


Paramtres :

out

Le flux de sortie.

header

Len-tte de la premire ligne du fichier stock.

Informations systme
Voici un autre exemple de lubiquit du jeu Properties. Les informations de votre systme sont
conserves dans un objet Properties renvoy par la mthode statique getProperties de la classe
System:
Properties sysprops = System.getProperties();

Pour accder une seule proprit, appelez


String value = System.getProperty(key);

Les applications dpourvues de gestionnaire de scurit bnficient dun accs complet ces informations ; or les applets et autres programmes douteux ne peuvent accder quaux cls suivantes :
java.version
java.vendor
java.vendor.url
java.class.version
os.name
os.version
os.arch
file.separator
path.separator
line.separator
java.specification.version
java.vm.specification.version
java.vm.specification.vendor
java.vm.specification.name
java.vm.version
java.vm.vendor
java.vm.name

Livre Java .book Page 616 Jeudi, 25. novembre 2004 3:04 15

616

Au cur de Java 2 - Notions fondamentales

Lorsquun applet tente de lire une autre cl (ou toutes les proprits), le systme dclenche une
exception de scurit.
INFO
Les noms des proprits systme sont librement accessibles dans le fichier security/ java.policy du rpertoire
runtime Java.

Le programme de lExemple 10.15 affiche les paires cl/valeur de lobjet Properties, qui conserve
les proprits systme.
Ci-aprs, un exemple de ce qui safficherait si vous excutiez le programme, toutes les valeurs stockes
dans cet objet Properties (laffichage refltera bien sr les paramtres de votre machine) :
#System Properties
#Sat May 08 08:27:34 PDT 2004
java.runtime.name=Java(TM) 2 Runtime Environment, Standard Edition
sun.boot.library.path=/usr/local/jdk5.0/jre/lib/i386
java.vm.version=5.0
java.vm.vendor=Sun Microsystems Inc.
java.vendor.url=http\://java.sun.com/
path.separator=\:
java.vm.name=Java HotSpot(TM) Client VM
file.encoding.pkg=sun.io
user.country=US
sun.os.patch.level=unknown
java.vm.specification.name=Java Virtual Machine Specification
user.dir=/home/cay/books/cj5/corejava/v1/v1ch10/SystemInfo
java.runtime.version=5.0
java.awt.graphicsenv=sun.awt.X11GraphicsEnvironment
java.endorsed.dirs=/usr/local/jdk5.0/jre/lib/endorsed
os.arch=i386
java.io.tmpdir=/tmp
line.separator=\n
java.vm.specification.vendor=Sun Microsystems Inc.
os.name=Linux
java.library.path=/usr/local/jdk5.0/jre/lib/i386/client\:/usr/local/jdk5.0/
jre/lib/i386\:/usr/local/jdk5.0/jre/../lib/ i386
java.specification.name=Java Platform API Specification
java.class.version=48.0
sun.management.compiler=HotSpot Client Compiler
java.util.prefs.PreferencesFactory=java.util.prefs.FileSystemPreferencesFactory
os.version=2.4.20-8
user.home=/home/cay
user.timezone=America/Los_Angeles
java.awt.printerjob=sun.print.PSPrinterJob
file.encoding=UTF-8
java.specification.version=5.0
java.class.path=.
user.name=cay
java.vm.specification.version=1.0
java.home=/usr/local/jdk5.0/jre
sun.arch.data.model=32
user.language=en
java.specification.vendor=Sun Microsystems Inc.
java.vm.info=mixed mode

Livre Java .book Page 617 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

617

java.version=5.0
java.ext.dirs=/usr/local/jdk5.0/jre/lib/ext
sun.boot.class.path=/usr/local/jdk5.0/jre/lib/rt.jar\:/usr/local/jdk5.0/jre/
lib/i18n.jar\:/usr/local/jdk5.0/jre/lib/ sunrsasign.jar\:/usr/local/jdk5.0/
jre/lib/jsse.jar\:/usr/local/jdk5.0/jre/lib/jce.jar\:/usr/local/jdk5.0/jre/
lib/charsets.jar\:/usr/local/jdk5.0/jre/classes
java.vendor=Sun Microsystems Inc.
file.separator=/
java.vendor.url.bug=http\://java.sun.com/cgi-bin/bugreport.cgi
sun.io.unicode.encoding=UnicodeLittle
sun.cpu.endian=little
sun.cpu.isalist=

Exemple 10.15 : SystemInfo.java


import java.applet.*;
import java.io.*;
import java.util.*;
/**
Ce programme affiche toutes les proprits systme.
*/
public class SystemInfo
{
public static void main(String args[])
{
try
{
Properties sysprops = System.getProperties();
sysprops.store(System.out, "System Properties");
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
java.lang.System 1.0
Properties getProperties()

Rcupre toutes les proprits systme. Lapplication doit avoir lautorisation de rcuprer
toutes les proprits, faute de quoi une exception de scurit est dclenche.

String getProperty(String key)

Rcupre la proprit systme avec le nom de cl donn. Lapplication doit avoir lautorisation
de rcuprer la proprit, faute de quoi une exception de scurit est dclenche.

LAPI Preferences
Comme vous lavez vu, la classe Properties simplifie le chargement et lenregistrement des
informations de configuration. Toutefois, lutilisation des fichiers de proprit prsente plusieurs
inconvnients :
m

Les fichiers de configuration ne peuvent pas toujours tre stocks au mme endroit que le
programme car cet emplacement nest pas inscriptible. Il peut sagir par exemple dun rpertoire
en lecture seule ou dun fichier JAR.

Livre Java .book Page 618 Jeudi, 25. novembre 2004 3:04 15

618

Au cur de Java 2 - Notions fondamentales

Plusieurs utilisateurs peuvent vouloir configurer la mme application de diffrentes manires.

Les fichiers de configuration peuvent tre stocks dans le rpertoire de base de lutilisateur. Or
certains systmes dexploitation (comme Windows 9x) ne connaissent pas les rpertoires de
base.

Il nexiste pas de convention de dnomination standard pour les fichiers de configuration, do


laugmentation de la probabilit des conflits de noms lorsque les utilisateurs installent plusieurs
applications Java.

Certains systmes dexploitation disposent dun rfrentiel central pour les informations de configuration. Lexemple le plus rput est la Base de registre de Microsoft Windows. La classe Preferences
du JDK 1.4 fournit ce systme sans rapport avec la plate-forme. Sous Windows, la classe Preferences utilise la Base de registre des fins de stockage ; sous Linux, les informations sont stockes
dans un systme de fichiers local. Bien entendu, limplmentation du rfrentiel est transparente
pour le programmeur qui utilise la classe Preferences.
Le rfrentiel Preferences possde une structure darborescence comprenant des noms de
chemin de nud, comme /com/mycompany/myapp. Comme pour les noms de package, on vitera
les conflits de noms en faisant dbuter les chemins par des noms de domaine inverss. En fait, les
concepteurs de lAPI suggrent de faire concorder les chemins de nud de configuration avec
les noms des packages.
Chaque nud du rfrentiel possde une table spare de paires cl/valeur que vous pouvez utiliser
pour stocker des nombres, des chanes ou des tableaux doctets. Toutefois, aucune disposition na t
prise pour stocker des objets srialisables. Les concepteurs dAPI ont considr que le format de
srialisation tait trop fragile pour le stockage long terme. Bien entendu, si vous pensez diffremment,
vous pouvez enregistrer les objets srialiss dans des tableaux doctets.
Pour plus de flexibilit, plusieurs arborescences parallles ont t cres. Chaque utilisateur du
programme en possde une, et il en existe une supplmentaire intitule arborescence du systme
et destine recueillir les paramtres communs tous les utilisateurs. La classe Preferences
utilise la notion "dutilisateur courant" du systme dexploitation pour accder larborescence
approprie de lutilisateur.
Pour accder un nud de larborescence, commencez par la racine de lutilisateur ou du systme :
Preferences root = Preferences.userRoot();

ou :
Preferences root = Preferences.systemRoot();

Puis accdez au nud. Vous pouvez simplement fournir un nom de chemin de nud :
Preferences node = root.node("/com/mycompany/myapp");

Il existe un raccourci commode pour rcuprer un nud dont le nom du chemin est gal au nom du
package dune classe. Prenez simplement un objet de cette classe et appelez
Preferences node = Preferences.userNodeForPackage(obj.getClass());

ou
Preferences node = Preferences.systemNodeForPackage(obj.getClass());

Gnralement, obj sera la rfrence this.

Livre Java .book Page 619 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

619

Lorsque vous avez un nud, vous avez accs la table cl/valeur avec les mthodes
String get(String key, String defval)
int getInt(String key, int defval)
long getLong(String key, long defval)
float getFloat(String key, float defval)
double getDouble(String key , double defval)
boolean getBoolean(String ke y, boolean defval)
byte[] getByteArray(String key, byte[] defval )

Sachez que vous devez spcifier une valeur par dfaut lorsque vous lisez les informations, au cas o
les donnes du rfrentiel ne seraient pas disponibles. Les paramtres par dfaut sont ncessaires
pour diverses raisons. Il est possible que des donnes manquent si lutilisateur na jamais prcis de
prfrence, certaines plates-formes contraintes par les ressources pourraient ne pas avoir de rfrentiel,
et des priphriques mobiles pourraient tre dconnects du rfrentiel.
A linverse, vous pouvez crire des donnes dans le rfrentiel avec des mthodes put, comme ceci :
put(String key, String value)
putInt(String key, int value)

Etc.
Vous pouvez numrer toutes les touches stockes dans un nud avec la mthode
String[] keys

Mais il nexiste actuellement aucune manire de dcouvrir le type de la valeur dune cl particulire.
Les rfrentiels centraux comme la Base de registre Windows souffrent gnralement de deux
problmes.
m

Ils se transforment en "dpotoirs", remplis dinformations obsoltes.

Les donnes de configuration sont entremles dans le rfrentiel, ce qui complique le dplacement
des prfrences vers une nouvelle plate-forme.

La classe Preferences apporte une solution au deuxime problme. Vous pouvez exporter les prfrences dune sous-arborescence (ou, moins souvent, dun seul nud) en appelant les mthodes
void exportSubtree(OutputStream out)
void exportNode(OutputStream out)

Les donnes sont enregistres au format XML. Vous pouvez les importer dans un autre rfrentiel en
appelant
void importPreferences(InputStream in)

Voici un exemple de fichier :


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
<preferences EXTERNAL_XML_VERSION="1.0">
<root type="user">
<map/>
<node name="com">
<map/>
<node name="horstmann">
<map/>
<node name="corejava">
<map>
<entry key="left" value="11"/>

Livre Java .book Page 620 Jeudi, 25. novembre 2004 3:04 15

620

Au cur de Java 2 - Notions fondamentales

<entry key="top" value="9"/>


<entry key="width" value="453"/>
<entry key="height" value="365"/>
</map>
</node>
</node>
</node>
</root>
</preferences>

Si votre programme utilise des prfrences, offrez vos utilisateurs lopportunit de les exporter
et de les importer, pour quils puissent facilement migrer leurs paramtres dun ordinateur un
autre. Le programme de lExemple 10.16 dmontre cette technique. Le programme enregistre
simplement la position et la taille de la fentre principale. Tentez de redimensionner la fentre,
puis quittez lapplication et redmarrez-la. La fentre saffichera comme vous lavez laisse en la
quittant.
Exemple 10.16 : PreferencesTest.java
import
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.io.*;
java.util.logging.*;
java.util.prefs.*;
javax.swing.*;
javax.swing.event.*;

/**
Un programme pour tester les paramtres de prfrence. Le programme
se souvient de la position et de la taille du cadre.
*/
public class PreferencesTest
{
public static void main(String[] args)
{
PreferencesFrame frame = new PreferencesFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre qui restaure la position et la taille partir des
prfrences utilisateur et actualise les prfrences la fermeture.
*/
class PreferencesFrame extends JFrame
{
public PreferencesFrame()
{
setTitle("PreferencesTest");
// rcuprer la position et la taille partir des prfrences
Preferences root = Preferences.userRoot();
final Preferences node = root.node("/com/horstmann/corejava");
int left = node.getInt("left", 0);
int top = node.getInt("top", 0);
int width = node.getInt("width", DEFAULT_WIDTH);

Livre Java .book Page 621 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

int height = node.getInt("height", DEFAULT_HEIGHT);


setBounds(left, top, width, height);
// configurer le slecteur des fichiers XML
final JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
// accepter tous les fichiers se terminant par .xml
chooser.setFileFilter(new
javax.swing.filechooser.FileFilter()
{
public boolean accept(File f)
{
return f.getName().
toLowerCase().endsWith(".xml") || f.isDirectory();
}
public String getDescription()
{
return "XML files";
}
});

// configurer les menus


JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem exportItem = new JMenuItem("Export preferences");
menu.add(exportItem);
exportItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
if(chooser.showSaveDialog(
PreferencesFrame.this) == JFileChooser.APPROVE_OPTION)
{
try
{
OutputStream out = new
FileOutputStream(chooser.getSelectedFile());
node.exportSubtree(out);
out.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
});
JMenuItem importItem = new JMenuItem("Import preferences");
menu.add(importItem);
importItem.addActionListener(new
ActionListener()

621

Livre Java .book Page 622 Jeudi, 25. novembre 2004 3:04 15

622

Au cur de Java 2 - Notions fondamentales

{
public void actionPerformed(ActionEvent event)
{
if(chooser.showOpenDialog(
PreferencesFrame.this) == JFileChooser.APPROVE_OPTION)
{
try
{
InputStream in = new FileInputStream(
chooser.getSelectedFile());
node.importPreferences(in);
in.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
});
JMenuItem exitItem = new JMenuItem("Exit");
menu.add(exitItem);
exitItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
node.putInt("left", getX());
node.putInt("top", getY());
node.putInt("width", getWidth());
node.putInt("height", getHeight());
System.exit(0);
}
});
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
java.util.prefs.Preferences 1.4

Preferences userRoot()

Renvoie le nud de prfrences racine de lutilisateur du programme dappel.

Preferences systemRoot()

Renvoie le nud de prfrences racine du systme.

Preferences node(String path)

Renvoie un nud accessible depuis le nud courant par le chemin donn. Si le chemin est
absolu (cest--dire quil commence par un /), cela implique que le nud commence la racine
de larborescence contenant ce nud de prfrence. Sil nexiste pas de nud avec le chemin
donn, il est cr.

Livre Java .book Page 623 Jeudi, 25. novembre 2004 3:04 15

Chapitre 10

Dployer des applets et des applications

623

Preferences userNodeForPackage(Class cl)


Preferences systemNodeForPackage(Class cl)

Renvoient un nud dans larborescence de lutilisateur actuel o larborescence du systme dont


le chemin de nud absolu correspond au nom du package de la classe cl.

String[] keys()

Renvoie toutes les cls appartenant ce nud.

String get(String key, String defval)


int getInt(String key, int defval)
long getLong(String key, long defval)
float getFloat(String key, float defval)
double getDouble(String key, double defval)
boolean getBoolean(String key, boolean defval)
byte[] getByteArray(String key, byte[] defval)

Ces mthodes renvoient la valeur associe la cl donne ou la valeur par dfaut fournie sil ny
a pas de valeur associe la cl, ou si la valeur associe nest pas du type correct, ou encore si la
banque des prfrences nest pas disponible.

void
void
void
void
void
void
void

put(String key, String value)


putInt(String key, int value)
putLong(String key, long value)
putFloat(String key, float value)
putDouble(String key, double value)
putBoolean(String key, boolean value)
putByteArray(String key, byte[] value)

Ces mthodes stockent une paire cl/valeur avec ce nud.

void exportSubtree(OutputStream out)

Ecrit les prfrences de ce nud et de ses enfants dans le flux spcifi.

void exportNode(OutputStream out)

Ecrit les prfrences de ce nud (mais pas de ses enfants) dans le flux spcifi.

void importPreferences(InputStream in)

Importe les prfrences contenues dans le flux spcifi.


Ainsi se termine notre tude du dploiement de logiciels dans Java. Au chapitre suivant, vous
apprendrez comment utiliser les exceptions pour prciser au programme ce quil doit faire en cas de
problme lors de lexcution. Nous vous fournirons galement les trucs et astuces pour tester et
dboguer, de faon minimiser les difficults en cours dexcution.

Livre Java .book Page 624 Jeudi, 25. novembre 2004 3:04 15

Livre Java .book Page 625 Jeudi, 25. novembre 2004 3:04 15

11
Exceptions et mise au point
Au sommaire de ce chapitre

Le traitement des erreurs


Capturer les exceptions
Quelques conseils sur lutilisation des exceptions
La consignation
Utiliser les assertions
Les techniques de mise au point
Utiliser un dbogueur
Dans un monde parfait, les utilisateurs saisissent leurs donnes dans la forme attendue. Leurs
fichiers existent et le programme ne contient pas de bogues. Jusqu prsent, nous avons suppos que
nous habitions ce monde parfait. Il est grand temps de faire connaissance avec les mcanismes
que possde Java pour affronter le monde rel et les programmes bogus.
La dcouverte dune erreur est toujours dplaisante. Si les travaux de lutilisateur sont perdus du fait
dune erreur de programmation ou dune circonstance extrieure au programme, votre programme
risque dtre purement et simplement abandonn. Il faut au minimum dans tous les cas :
m

envoyer un message derreur ;

sauvegarder lensemble du travail en cours ;

permettre une sortie correcte du programme.

Dans les situations exceptionnelles telles que des donnes dentre fausses pouvant potentiellement
planter le programme, Java utilise un mcanisme dinterception des erreurs appel de faon assez
image traitement des exceptions. Le traitement des exceptions en Java est trs voisin de celui de
C++ et de Delphi. La premire partie de ce chapitre traite des exceptions de Java.
Dans la seconde partie, nous verrons comment dnicher les bogues dun programme avant quils ne
provoquent les exceptions lexcution. La recherche des bogues en est malheureusement reste au
Moyen Age dans le JDK. Nous donnons quelques astuces et des outils pour amliorer la situation et

Livre Java .book Page 626 Jeudi, 25. novembre 2004 3:04 15

626

Au cur de Java 2 - Notions fondamentales

expliquons lemploi en dernier recours du dbogueur en ligne de commande. Le dveloppeur srieux


trouvera des environnements de dbogage puissants dans des produits comme Eclipse, NetBeans et
JBuilder. Nous vous prsentons une introduction au dbogueur Eclipse.

Le traitement des erreurs


Supposons une erreur survenant lexcution dans un programme crit en Java. La cause de lerreur
peut tre un fichier contenant une information incorrecte, une connexion rseau dfaillante, un index
de tableau invalide (quelle horreur !) ou lemploi dune rfrence un objet non initialise. Lutilisateur sattend toujours un comportement logique des programmes dans tous les cas derreurs.
Le programme devra donc, lorsquune opration ne peut tre poursuivie en raison dune erreur :
m

soit revenir un tat dfini et offrir lutilisateur la possibilit dexcuter dautres commandes ;

soit permettre lutilisateur de sauvegarder son travail et de sortir normalement du programme.

Cela nest pas toujours vident. En effet, le code qui dcle un tat derreur (ou mme celui qui en
est lorigine) est en principe sans relation directe avec le code qui ramne les donnes dans un tat
connu ou celui qui sauvegarde le travail de lutilisateur et procde une sortie de programme
normale. Lobjectif du traitement des exceptions est prcisment de pouvoir transfrer le contrle de
lendroit o se produit une erreur un gestionnaire derreur capable daffronter cette situation. Pour
traiter des situations exceptionnelles, le programme doit prendre en compte chaque erreur possible et
les problmes qui y sont lis. Quels types de problmes faut-il prvoir ?
m

Les fautes de saisie de lutilisateur. Non contents de faire des fautes de frappe, certains utilisateurs suivent leur ide sans tenir compte des messages les guidant. Par exemple, le programme
demande un utilisateur de corriger une URL dont la syntaxe est mauvaise. Le programme devra
vrifier de nouveau la syntaxe, mais galement faire lhypothse que lutilisateur na pas entr de
correction. Sans quoi, linterface rseau mettra son tour des messages derreur.

Les erreurs du matriel. Le matriel est parfois non oprationnel. Limprimante peut tre
dconnecte ; une page Web peut se trouver inaccessible de faon temporaire. Les priphriques
sinterrompent souvent au milieu dun travail : par exemple, une imprimante va manquer de
papier.

Les contraintes physiques. Un disque peut tre plein ou on peut manquer de mmoire en cours
dexcution.

Les erreurs de programmation. Dans certains cas, une mthode peut tre erratique : soit elle
donnera des rsultats faux, soit elle appellera dautres mthodes de faon incorrecte. Des exemples dappels errons conduisent aux situations suivantes : une mthode calcule un index de
tableau incorrect, recherche une entre inexistante dans une table de hachage ou encore essaie
de dpiler une pile vide.

La mthode classique de raction une erreur consiste renvoyer un code derreur particulier qui
sera trait par la mthode appelante. Par exemple, une mthode lisant dans un fichier renverra une
valeur 1 pour marquer une fin de fichier. Ce moyen peut tre efficace pour se sortir de nombreux cas
exceptionnels. Une autre valeur de retour traditionnelle pour indiquer une erreur est la rfrence
null (le Chapitre 10 donne cet exemple pour la mthode getParameter de la classe Applet).

Livre Java .book Page 627 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

627

Il nest pas toujours possible de renvoyer un code derreur. Par exemple, il peut ne pas exister de
moyen vident pour distinguer une bonne donne dune donne incorrecte : une mthode renvoyant
un entier ne peut utiliser la valeur 1 pour signaler une erreur (cette valeur pourrait tout fait tre
autorise).
Nous avons vu au Chapitre 5 que Java laisse la possibilit dintroduire dans une mthode une sortie
anormale dans le cas o la mthode ne peut excuter ce qui a t prvu. La mthode ne renvoie pas
de valeur particulire, mais utilise la clause throws avec un objet encapsulant linformation derreur.
Il faut bien voir que la mthode se termine immdiatement : en particulier, elle ne renvoie pas la
valeur attendue ni aucune valeur, dailleurs. De plus, Java ne rend pas la main au code appelant ; le
mcanisme du traitement dexceptions va lancer une recherche dun gestionnaire dexception capable
de traiter cette situation derreur.
Les exceptions obissent une syntaxe particulire et se trouvent dans une hirarchie dhritage
part. Commenons par observer la syntaxe, puis nous donnerons quelques exemples concrets
dutilisation.

Le classement des exceptions


En Java, un objet exception est toujours une instance dune sous-classe de Throwable. En particulier,
comme nous ltudierons plus loin, il est possible de crer ses propres classes dexceptions dans le
cas o les classes dfinies dans Java ne conviendraient pas.
La Figure 11.1 donne un diagramme simplifi de la hirarchie des exceptions en Java.

Throwable

Error

Exception

IOException

Runtime
Exception

Figure 11.1
La hirarchie des exceptions en Java.

On voit que toutes les exceptions descendent de Throwable, puis la hirarchie se spare immdiatement
en une branche Error et une branche Exception.
La hirarchie Error dcrit les erreurs internes ou le manque de ressources dans le systme dexcution de Java. En principe, vous ne devez pas faire un throw dun objet de ce type. On ne peut pas

Livre Java .book Page 628 Jeudi, 25. novembre 2004 3:04 15

628

Au cur de Java 2 - Notions fondamentales

grand-chose dans le cas de ce type derreur, au demeurant assez rare, sauf en informer lutilisateur
et tenter de mettre fin au programme de faon adquate.
En programmation Java, seule importe la hirarchie Exception. Elle se spare galement en deux
branches : les exceptions drivant de RuntimeException et les autres. Voici la rgle gnrale : une
exception RuntimeException se produit toujours en raison dune erreur de programmation. Toutes
les autres exceptions ont une cause externe votre programme, par exemple une erreur dentre/
sortie.
Les exceptions hritant de RuntimeException traitent des problmes comme ceux-ci :
m

un mauvais transtypage ;

un accs un tableau en dehors des limites ;

lemploi dun pointeur null.

Les exceptions nhritant pas de RuntimeException comprennent :


m

lire au-del de la fin dun fichier ;

accder une URL incorrecte ;

rechercher un objet Class en donnant un nom ne correspondant aucune classe existante.

On peut donner une rgle excellente : "Sil sagit dune RuntimeException, ce ne peut tre que de
votre faute." Ainsi, vous auriez pu viter une ArrayIndexOutOfBoundsException en contrlant que
lindice du tableau reste bien lintrieur du tableau. De mme, lexception NullPointerException ne
peut se produire si vous vitez dutiliser une variable de valeur null.
Mais que peut-on faire dans le cas dune URL incorrecte ? Comment mme dterminer ce que signifie incorrecte ? Pour un navigateur donn, certains types dURL seront inappropris. Par exemple,
Netscape accepte une URL mailto, alors que le visualisateur dapplet en est incapable. Cette notion
dpend donc de lenvironnement dexcution et non uniquement du programme.
Les spcifications du langage Java baptisent toute exception drivant de la classe Error ou de la
classe RuntimeException du nom dexception hors contrle (unchecked). Les autres exceptions
sont dites sous contrle (checked). Nous utiliserons cette terminologie pertinente. En outre, le
compilateur vrifie que vous apportiez des gestionnaires dexception pour toutes les exceptions sous
contrle.
INFO
Le nom RuntimeException est ambigu : il va de soi que toutes les erreurs dont nous parlons se produisent
lexcution.

INFO C++
Si vous connaissez bien la hirarchie des exceptions de la bibliothque C++ standard (hirarchie beaucoup moins
tendue), les choses doivent vous paratre confuses. C++ possde deux classes dexceptions de base, runtime_error
et logic_error. La classe logic_error est lquivalent de notre classe RuntimeException et correspond galement des erreurs de logique du programme. La classe runtime_error est la classe de base pour les exceptions
provenant de causes imprvisibles. Elle traite les exceptions qui pour Java ne sont pas du type RuntimeException.

Livre Java .book Page 629 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

629

Signaler les exceptions sous contrle


Une mthode Java va crer une exception si elle rencontre une situation quelle ne peut traiter. Il y a
deux points considrer : la mthode ne doit pas seulement informer le compilateur Java des valeurs
quelle va renvoyer, mais galement linformer des erreurs possibles. Par exemple, un programme
devant lire un fichier reconnat que le fichier pourrait ne pas exister ou encore quil pourrait tre vide.
Le code devant traiter les informations dun fichier va donc devoir informer le compilateur quil peut
crer une variante des IOException.
Cest dans len-tte dune mthode que lon informe des exceptions sous contrle. Voici titre
dexemple len-tte de lun des constructeurs de la classe FileInputStream appartenant la bibliothque standard (voir Chapitre 12 sur les flux) :
public FileInputStream(String name) throws FileNotFoundException

Len-tte indique que ce constructeur renvoie un objet FileInputStream dun paramtre String,
mais quil peut aussi en cas danomalie lancer une FileNotFoundException. Si le constructeur
vient se trouver dans cet tat fcheux, son appel ninitialisera pas un nouvel objet FileInputStream mais excutera la clause throws sur un objet de la classe FileNotFoundException. En ce
cas, le systme dexcution va lancer la recherche dun gestionnaire dexception capable de traiter
les objets FileNotFoundException.
Quand on crit une mthode, il nest pas ncessaire de signaler tous les objets disponibles dans un
throws. Pour savoir sil faut signaler un type dexception dans la clause throws dune mthode
que vous vous proposez dcrire, examinons les quatre situations suivantes qui conduisent des
exceptions :
1. Vous appelez une mthode qui lance une exception sous contrle, comme le constructeur
FileInputStream.
2. Vous identifiez un cas derreur et vous lancez une exception sous contrle par linstruction
throw (nous verrons linstruction throw dans la section suivante).
3. Vous faites une erreur de programmation, comme a [-1] = 0 qui fait surgir une exception hors
contrle telle que ArrayIndexOutOfBoundsException.
4. Il se produit une erreur interne dans la machine virtuelle ou dans la bibliothque de runtime.
Dans les deux premiers cas, il faut imprativement informer les programmeurs qui se serviront de
votre mthode que son utilisation peut provoquer lapparition dune exception. En effet, toute
mthode lanant une exception peut devenir un pige fatal, puisquen labsence dun gestionnaire de
cette exception, le programme sarrtera.
A linstar des mthodes qui se trouvent dans les classes fournies avec Java, on dclare quune
mthode peut lancer une exception par une spcification dexception dans len-tte de mthode :
class MyAnimation
{
. . .
public Image loadImage(String s) throws IOException
{
. . .
}
}

Livre Java .book Page 630 Jeudi, 25. novembre 2004 3:04 15

630

Au cur de Java 2 - Notions fondamentales

Lorsquune mthode traite plusieurs exceptions sous contrle, elles doivent toutes figurer dans lentte. Les noms des mthodes sont spars par une virgule :
class MyAnimation
{
. . .
public Image loadImage(String s)
throws EOFException, MalformedURLException
{
. . .
}
}

En revanche, il ne faut pas signaler les erreurs internes de Java, plus prcisment les exceptions hritant de Error. Ces exceptions peuvent provenir de nimporte quelle partie dun programme et sont
hors de votre contrle.
De mme, il ne faut pas signaler les exceptions hors contrle hritant de RuntimeException:
class MyAnimation
{
. . .
void drawImage(int i)
throws ArrayIndexOutOfBoundsException // NON!!!
{
. . .
}
}

Ces types derreurs dexcution ne peuvent tre contrls que par vous. Si vous tes obsd par les
valeurs errones des indices de tableaux, rien ne vous empche de passer le temps ncessaire les
traquer au lieu de les signaler.
En rsum, une mthode doit dclarer toutes les exceptions sous contrle. Les exceptions hors
contrle soit sont au-del de ce qui vous est accessible (Error), soit rsultent de situations que
vous nauriez pas d laisser se produire (RuntimeException). Si vous ne russissez pas dclarer toutes les exceptions sous contrle de faon adapte, le compilateur affichera un message
derreur.
Bien sr, comme vous lavez dj remarqu travers quelques exemples, au lieu de dclarer les
exceptions, vous pouvez aussi les intercepter. Dans ce cas, lexception ne sera pas expulse hors de
la mthode, et il nest pas ncessaire dutiliser des spcifications de lancement. Vous verrez plus loin
comment dcider sil faut intercepter une exception ou laisser quelquun dautre le faire votre
place.
ATTENTION
Une mthode surcharge dune superclasse ne peut pas lancer plus dexceptions sous contrle que la mthode
correspondante de sa superclasse (elle peut lancer des exceptions plus spcifiques ou aucune dans la mthode de la
sous-classe). Si la mthode de la superclasse ne lance aucune exception sous contrle, il en va de mme pour la sousclasse. Par exemple, si vous surchargez JComponent.paintComponent, votre mthode paintComponent ne
pourra lancer aucune exception sous contrle si la mthode de la superclasse nen lance aucune.

Livre Java .book Page 631 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

631

Quand une mthode dclare quelle lance une exception, instance dune classe dexceptions donne,
elle peut alors lancer une exception de cette classe et de nimporte laquelle de ses sous-classes. Par
exemple, le constructeur FileInputStream aurait pu prvenir quil lance une IOException. Nous
ne savons pas ce niveau de quel type de IOException il sagit : ce peut tre une simple IOException
ou un objet de lune des diverses sous-classes comme une FileNotFoundException.
INFO C++
Le spcifieur throws est identique au spcifieur throw de C++, avec une diffrence majeure : en C++, les spcifieurs
throw sont pris en compte au moment de lexcution et non la compilation. Autrement dit, le compilateur C++ ne
contrle pas les spcifications dexceptions. Si une exception nappartenant pas la liste throw est lance, la fonction
unexpected est appele, et par dfaut, le programme se termine.
Une fonction C++ peut lancer nimporte quelle exception en labsence dune spcification throw. En Java, une
mthode sans spcifieur throws ne peut lancer aucune exception.

Comment lancer une exception


Supposons quil se produise une situation tout fait anormale pour votre programme. Vous possdez
une mthode readData qui lit un fichier dont len-tte affirme :
Content-length: 1024

Vous rencontrez une fin de fichier au bout de 733 caractres ; vous dcidez que cette situation est
assez anormale pour mriter une exception.
Il vous faut dcider du type dexception. Un bon choix consiste raffiner IOException. En feuilletant la documentation de lAPI Java, vous lisez "Signals that an EOF has been reached unexpectedly
during input" comme description dune EOFException. Cest ce quil vous faut puisque cela
"signale une fin de fichier anormale au cours dune entre". Voici comment la lancer :
throw new EOFException();

ou encore son quivalent :


EOFException e = new EOFException();
throw e;

Voici une vue de lensemble :


String readData(Scanner in) throws EOFException
{
. . .
while (. . .)
{
if (!in.hasNext()) // EOF rencontre
{
if (n < len)
throw new EOFException();
}
. . .
}
return s;
}

Livre Java .book Page 632 Jeudi, 25. novembre 2004 3:04 15

632

Au cur de Java 2 - Notions fondamentales

EOFException possde un second constructeur qui accepte un argument de type chane. On peut en
profiter pour dcrire plus en dtail la situation dexception :
String gripe = "Content-length: "+len+", Received: "+n;
throw new EOFException(gripe);

Nous voyons quil est ais de lancer une exception si lune des classes dexceptions existantes fait le
travail, car en ce cas il suffit de :
1. trouver la classe dexceptions approprie ;
2. faire un objet de cet objet ;
3. le lancer.
Lorsquune mthode lance une exception, elle ne rend pas la main son appelant. Cela implique
quil est inutile de se proccuper dune valeur de retour par dfaut ou encore dun code derreur.
INFO C++
Lancer une exception sopre de la mme faon en C++ et en Java, avec une petite diffrence. En Java, on ne peut
lancer que des objets des sous-classes de Throwable. En C++, on peut lancer des valeurs de nimporte quel type.

Crer des classes dexception


Il se peut que votre code rencontre un problme qui ne soit dcrit de faon adquate par aucune des
classes dexception standard. En ce cas, il est assez facile de crer votre propre classe dexception.
Il suffit de la faire driver de Exception ou dune de ses classes enfants telles que IOException. Il est
dusage de donner la fois un constructeur par dfaut et un constructeur qui contient un message
dtaill (la mthode toString de la superclasse Throwable affiche ce message dtaill, qui est trs
utile la mise au point) :
class FileFormatException extends IOException
{
public FileFormatException() {}
public FileFormatException(String gripe)
{
super(gripe);
}
}

Vous pouvez maintenant lancer votre propre type dexception :


String readData(BufferedReader in) throws FileFormatException
{
. . .
while (. . .)
{
if (ch == -1) // EOF rencontre
{
if (n < len)
throw new FileFormatException();
}
. . .
}
return s;
}

Livre Java .book Page 633 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

633

java.lang.Throwable 1.0

Throwable()

Construit un nouvel objet Throwable sans message dtaill.

Throwable(String message)

Construit un nouvel objet Throwable avec le message dtaill spcifi. Par convention, toutes les
classes dexceptions drives supportent la fois un constructeur par dfaut et un constructeur
comprenant un message dtaill.

String getMessage()

Rcupre le message dtaill de lobjet Throwable.

Capturer les exceptions


Les exceptions sont faciles lancer ; il faut maintenant prvoir un code pour les capturer, ce qui
demande un peu plus dattention.
Si une exception se produit sans tre capture nulle part, le programme se termine et affiche un
message donnant le type de lexception ainsi quune trace de la pile. Un programme graphique (que
ce soit un applet ou une application) intercepte les exceptions, affiche les messages de trace de la
pile, puis retourne sa boucle de traitement de linterface utilisateur (lorsque vous dboguez un
programme graphique, il est conseill de garder lcran la fentre de lancement non rduite).
Pour capturer une exception, on crit un bloc try/catch, dont la forme la plus simple est la suivante :
try
{
votre code
plus de code
et encore du code
}
catch(ExceptionType e)
{
le gestionnaire pour ce type dexception
}

Si une exception est lance dans le bloc try de la classe spcifie par la clause catch, alors :
1. Le programme abandonne lexcution du code restant dans le bloc try.
2. Le programme excute le code du gestionnaire situ dans la clause catch.
Si aucune des instructions du bloc try ne lance une exception, le programme ignore la clause catch.
Si une instruction lintrieur dune mthode lance une exception dun autre type que celui qui est
spcifi par la clause catch, la mthode sinterrompt immdiatement (heureusement, lun de ses
appelants a dj introduit une clause catch pour ce type).
Pour observer cela en action, voici des instructions typiques pour lire lintrieur dun texte :
public void read(String filename)
{
try
{
InputStream in = new FileInputStream(filename);
int b;
while ((b = in.read())!=-1)

Livre Java .book Page 634 Jeudi, 25. novembre 2004 3:04 15

634

Au cur de Java 2 - Notions fondamentales

{
traitement de lentre
}
}
catch (IOException exception)
{
exception.printStackTrace();
}
}

Les instructions de la clause try sont sans mystre : elles lisent et traitent des lignes jusqu rencontrer
une fin de fichier. Comme vous pouvez le voir en examinant lAPI de Java, la mthode read peut
lancer une IOException. En ce cas, lensemble de la boucle while est ignor et la clause catch
force un traage de la pile. Pour un programme de peu dimportance, cela semble une faon adapte
de grer le problme des exceptions. Quel autre choix avons-nous ?
Bien souvent, il vaut mieux ne rien faire du tout et simplement passer lexception lappelant. Si une
erreur se produit dans la mthode read, on laisse lappelant de la mthode read sen inquiter ! Si nous
voulons suivre cette approche, nous devons signaler que la mthode peut lancer une IOException:
public void read(String filename) throws IOException
{
InputStream in = new FileInputStream(filename);
int b;
while ((b = in.read())!=-1)
{
traitement de lentre
}
}

Il faut se rappeler que le compilateur exige toujours un spcifieur throws. Si vous appelez une
mthode qui lance une exception sous contrle, il vous faut soit la grer soit la propager.
Quelle est la meilleure faon de procder ? En rgle gnrale, vous devriez rcuprer les exceptions
que vous savez grer et propager les autres. Dans ce dernier cas, il faut obligatoirement ajouter un
spcifieur throws pour signaler lappelant quune exception peut tre lance.
Reportez-vous la documentation de lAPI de Java pour voir quelle mthode lance des exceptions,
puis dcider de grer vous-mme vos exceptions ou de les ajouter la liste des throws. Il vaut bien
mieux renvoyer une exception au gestionnaire ad hoc que la museler.
Notez quil existe une exception cette rgle : lorsque vous crivez une mthode surchargeant une
mthode de sa superclasse qui ne lance pas dexception (telle paintComponent dans JComponent),
alors vous devez rcuprer toutes les exceptions sous contrle de la mthode. Vous navez pas le
droit de dclarer une mthode de sous-classe des spcifieurs throws qui ne soient dj prsents
dans la mthode de sa superclasse.
INFO C++
La capture des exceptions se fait de faon quasi identique en Java et en C++. Plus prcisment,
catch(Exception e) // Java

est quivalent
catch(Exception& e) // C++

Il ny pas dquivalent du catch(...) de C++, qui nest pas ncessaire en Java puisque que toutes les exceptions
drivent dune mme superclasse de base.

Livre Java .book Page 635 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

635

Capturer des exceptions multiples


On peut capturer des exceptions multiples dans un bloc try et les grer ensuite diffremment. Il faut
utiliser en ce cas une clause catch spcifique chaque type, comme dans cet exemple :
try
{
ici du code qui est susceptible
de lancer des exceptions
}
Catch (MalformedURLException e1)
{
action chaud pour les URL incorrectes
}
catch(UnknownHostException e2)
{
action chaud pour les htes inconnus
}
catch(IOException e3)
{
action chaud pour tous les autres problmes dE/S
}

Lobjet exception (e1, e2, e3) peut renseigner sur la nature de lexception. Pour en savoir plus sur
lobjet exception, on peut essayer
e3.getMessage()

qui renvoie un message derreur dtaill, sil existe, ou encore


e3.getClass().getName()

qui donne le type effectif de lobjet exception.

Relancer et enchaner les exceptions


Vous pouvez lancer une exception dans une clause catch. Cela se produit typiquement quand il faut
en changer le type. Si vous construisez un sous-systme qui sera utilis par dautres programmeurs,
vous utiliserez en toute logique un type dexception indiquant une panne du sous-systme, par exemple ServletException. Le code qui excute un servlet peut ne pas vouloir connatre le problme en
dtail, mais bien savoir que le servlet tait erron.
Voici comment intercepter une exception et la relancer :
try
{
accder la base de donnes
}
catch (SQLException e)
{
throw new ServletException("database error: " + e.getMessage());
}

Ici, lexception ServletException est construite avec le texte de son message. Depuis le JDK 1.4,
vous pouvez faire encore mieux et dfinir lexception dorigine comme tant la "cause" de la
nouvelle exception :
try
{

Livre Java .book Page 636 Jeudi, 25. novembre 2004 3:04 15

636

Au cur de Java 2 - Notions fondamentales

accder la base de donnes


}
catch (SQLException e)
{
Throwable se = new ServletException("database error");
se.setCause(e);
throw se;
}

Au moment de linterception, il est possible de rcuprer lexception dorigine :


Throwable e = se.getCause();

Cette technique demballage est fortement recommande. Elle vous permet de lancer des exceptions
de haut niveau dans des sous-systmes, sans pour autant perdre les dtails de la panne initiale.
ASTUCE
La technique demballage se rvle utile galement lorsque survient une exception sous contrle dans une
mthode qui nest pas autorise le faire. Vous pouvez intercepter cette exception et lemballer dans une exception
dexcution.

INFO
Plusieurs classes dexception, telles ClassNotFoundException, InvocationTargetException et RuntimeException, possdaient leurs propres schmas de chanage. Depuis le JDK 1.4, elles se conforment au mcanisme
de la "cause". Vous pouvez toujours rcuprer lexception chane en utilisant la mthode prcdente, mais vous
pouvez maintenant simplement appeler getCause. Malheureusement, lexception chane la plus souvent utilise
par les programmeurs dapplications, SQLException, na pas fait lobjet de cette mise jour.

La clause finally
Lorsque votre code lance une exception, il interrompt le code restant dans votre mthode et sort de
celle-ci. Cette situation peut causer un problme lorsque la mthode en question dtient une
ressource locale, quelle est la seule le savoir et que cette ressource doit tre dsalloue. Une solution peut tre de rcuprer et de relancer lensemble des exceptions. Mais cette solution est fastidieuse parce quil faudra dsallouer la ressource deux endroits : dans le code normal et dans le
code de traitement de lexception.
Java offre une meilleure solution, la clause finally. Nous montrons ici comment se dbarrasser
dun objet Graphics, comme il se doit. Si vous effectuez une programmation de base de donnes en
Java, vous devrez utiliser les mmes techniques pour mettre fin aux connexions la base de donnes.
Comme vous le verrez au Chapitre 4 du Volume 2, il est trs important de clore correctement toutes
les connexions, et ce mme en cas dexceptions.
Le code de la clause finally sexcute, quune exception ait t intercepte ou non. Dans lexemple
suivant, le programme se dbarrassera du contexte graphique dans tous les cas :
Graphics g = image.getGraphics();
try
{
// 1
ici du code qui est susceptible

Livre Java .book Page 637 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

637

de lancer des exceptions


// 2
}
catch (IOException e)
{
// 3
afficher la bote de dialogue derreur
// 4
}
finally
{
// 5
g.dispose();
}
// 6

Examinons les trois situations o le programme risque dexcuter la clause finally.


1. Le code ne lance pas dexception. Dans ce cas, le programme commence par excuter toutes les
instructions du bloc try. Il excute ensuite celles de la clause finally, puis passe lexcution
de la premire ligne suivant le bloc try. Autrement dit, lexcution passe par les points 1, 2, 5 et 6.
2. Le code lance une exception capture dans une clause catch, dans notre cas une IOException.
Pour cela, le programme excute lensemble des instructions du bloc try jusquau point o
lexception a t lance. Le code restant du bloc est alors ignor et le programme excute le code
de la clause catch correspondante, puis celui de la clause finally.
Si la clause catch ne lance pas dexception, le programme excute la premire ligne suivant le
bloc try. Dans ce scnario, lexcution passe par les points 1, 3, 4, 5 et 6.
Si elle lance une exception, lexception est renvoye lappelant de cette mthode et lexcution
passe par les points 1, 3 et 5 uniquement.
3. Le code lance une exception qui nest capture par aucune clause catch. A cet effet, le
programme excute lensemble du code du bloc try jusqu ce que lexception apparaisse. Le
reste du code situ dans le bloc try est ignor, et cest celui de la clause finally qui est excut.
Lexception est renvoye lappelant de la mthode. Lexcution passe par les points 1 et 5
uniquement.
On peut utiliser la clause finally sans clause catch, comme dans linstruction try suivante :
Graphics g = image.getGraphics();
try
{
ici du code qui est susceptible
de lancer des exceptions
}
finally
{
g.dispose();
}

La commande g.dispose de la clause finally est excute dans le bloc try, quune exception soit
rencontre ou non. Bien entendu, dans le cas o une exception est rencontre, elle est renvoye et
devra tre rcupre dans une autre clause catch.

Livre Java .book Page 638 Jeudi, 25. novembre 2004 3:04 15

638

Au cur de Java 2 - Notions fondamentales

Comme nous lexpliquons dans lastuce suivante, nous recommandons dutiliser la clause finally
de cette manire.
ASTUCE
Nous recommandons chaudement de dcoupler les blocs try/catch et try/finally. Votre code sera alors
beaucoup moins confus. Par exemple :
InputStream in = ...;
try
{
try
{
code susceptible de lancer une exception
}
finally
{
in.close();
}
}
catch (IOException e)
{
afficher la bote de dialogue derreur
}

Le bloc try interne na quune seule responsabilit : sassurer que le flux dentre soit ferm. Quant au bloc try
extrieur, il doit vrifier que les erreurs sont signales. Cette solution est plus claire, mais elle prsente aussi lavantage dtre plus fonctionnelle, puisque les erreurs de la clause finally sont bien rapportes.

ATTENTION
La clause finally provoque des rsultats inattendus lorsquelle contient des instructions return. Supposons que
vous quittiez au milieu dun bloc try avec une instruction return. Avant la mthode return, le contenu du bloc
finally est excut. Sil contient galement une instruction return, il masque la valeur dorigine du premier
return. Voici un exemple :
public static int f(int n)
{
try
{
int r = n * n;
return r;
}
finally
{
if (n == 2) return 0;
}
}

Si vous appelez f (2), alors le bloc try calcule r = 4 et excute linstruction return. Cependant la clause finally
est excute avant que la mthode ne soit finie dexcuter. La clause finally provoque un retour 0 de la
mthode, ignorant la valeur dorigine qui tait 4.

Livre Java .book Page 639 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

639

Parfois la clause finally rend la vie difficile, plus prcisment si la mthode de nettoyage peut
galement lancer une exception. Un cas typique consiste fermer un flux. Pour en savoir plus,
rendez-vous au Chapitre 12. Supposez que vous dsiriez vous assurer que vous terminez un flux
lorsquune exception surgit durant le flux du code en cours :
InputStream in = ...;
try
{
code susceptible de lancer
des exceptions
}
catch (IOException e)
{
afficher la bote de dialogue derreur
}
finally
{
in.close();
}

Maintenant, supposez que le code dans le bloc try lance une exception autre quune exception
IOException, ce qui est lintrt de lappelant du code. Le bloc finally sexcute et la mthode
close est appele. Elle peut elle-mme lancer une exception IOException! Lorsque cela se
produit, lexception dorigine est perdue et la seconde prend sa place. Cest tout fait contraire
lesprit de la gestion derreur.
Cest une ide excellente, autant que peu suivie par les concepteurs de la classe InputStream, que
dviter de lancer des exceptions dans les oprations de nettoyage telles que dispose, close, et
ainsi de suite, dont vous prvoyez lappel par les utilisateurs dans les blocs finally.
INFO C++
Il existe une diffrence fondamentale entre C++ et Java dans leur manire de grer les exceptions. Java ne possde
pas de destructeur : il ny a donc pas de vidage de la pile comme en C++. Ainsi, le programmeur Java doit introduire
lui-mme le code ncessaire pour grer les ressources dans les blocs finally. Comme Java possde un ramasse-miettes,
moins de ressources ncessitent dtre dsalloues directement.

Analyser les traces de piles


Une trace de pile (stack trace) est une liste des appels de mthodes en attente, figurant un moment
donn dans lexcution dun programme. Vous en avez certainement dj vu, puisquelles saffichent
lorsquun programme Java est termin par une exception non intercepte.
INFO
La trace de pile ne remonte que jusqu linstruction qui a dclench lexception, et non pas ncessairement la
racine de lerreur.

Livre Java .book Page 640 Jeudi, 25. novembre 2004 3:04 15

640

Au cur de Java 2 - Notions fondamentales

Avant le JDK 1.4, on accdait la description textuelle de la trace de pile en appelant la mthode
StrackTrace de la classe Throwable. Vous pouvez maintenant appeler la mthode getStackTrace,
qui renvoie un tableau dobjets StackTraceElement analyser dans votre programme. Par exemple :
Throwable t = new Throwable();
StackTraceElement[] frames = t.getStackTrace();
for (StackTraceElement frame: frames)
analyser le cadre

La classe StackTraceElement possde des mthodes lui permettant dobtenir le nom du fichier et le
numro de ligne, de mme que le nom de la classe et de la mthode de la ligne de code qui sexcute.
La mthode toString produit une chane o toutes ces informations ont t mises en forme.
Le JDK 5.0 ajoute la mthode statique Thread.getAllStackTraces, qui produit les traces de pile
de tous les threads. Voici comment utiliser cette mthode :
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
for (Thread t: map.keySet())
{
StackTraceElement[] frames = map.get(t);
analyser les cadres
}

Reportez-vous au Volume 2 pour en savoir plus sur les threads et sur linterface Map.
LExemple 11.1 affiche la trace de pile dune fonction factorielle rcurrente. Si vous calculez par
exemple factorial(3), laffichage sera :
factorial(3):
StackTraceTest.factorial(StackTraceTest.java:8)
StackTraceTest.main(StackTraceTest.java:23)
factorial(2):
StackTraceTest.factorial(StackTraceTest.java:8)
StackTraceTest.factorial(StackTraceTest.java:14)
StackTraceTest.main(StackTraceTest.java:23)
factorial(1):
StackTraceTest.factorial(StackTraceTest.java:8)
StackTraceTest.factorial(StackTraceTest.java:14)
StackTraceTest.factorial(StackTraceTest.java:14)
StackTraceTest.main(StackTraceTest.java:23)
return 1
return 2
return 6

Exemple 11.1 : StackTraceTest.java


import java.util.*;
/**
Un programme qui affiche une fonction de trace dun
appel de mthode rcurrent.
*/
public class StackTraceTest
{
/**
Calcule la factorielle dun nombre
@param n Un entier non-ngatif
@return n! = 1 * 2 * . . . * n
*/
public static int factorial(int n)

Livre Java .book Page 641 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

641

{
System.out.println("factorial(" + n + "):");
Throwable t = new Throwable();
StackTraceElement[] frames = t.getStackTrace();
for (StackTraceElement f: frames)
System.out.println(f);
int r;
if (n <= 1) r = 1;
else r = n * factorial(n - 1);
System.out.println("return " + r);
return r;
}
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Enter n: ");
int n = in.nextInt();
factorial(n);
}
}
java.lang.Throwable 1.0

Throwable(Throwable cause) 1.4


Throwable(String message, Throwable cause) 1.4

Construisent un Throwable avec la cause donne.

Throwable initCause(Throwable cause) 1.4

Dfinit la cause pour cet objet ou lance une exception si cet objet possde dj une cause.
Renvoie this.

Throwable getCause() 1.4

Rcupre lobjet exception qui a t dfini comme cause de cet objet ou null sil nexiste pas de
cause.

StackTraceElement[] getStackTrace() 1.4

Rcupre la trace de la pile dappels de la construction de cet objet.


java.lang.Exception 1.0

Exception(Throwable cause) 1.4


Exception(String message, Throwable cause)

Construisent une exception avec une cause donne.


java.lang.RuntimeException 1.0

RuntimeException(Throwable cause) 1.4


RuntimeException(String message, Throwable cause) 1.4

Construisent une exception RuntimeException avec une cause donne.


java.lang.StackTraceElement 1.4

String getFileName()

Rcupre le nom du fichier source contenant le point dexcution de cet lment ou null si ces
informations ne sont pas disponibles.

Livre Java .book Page 642 Jeudi, 25. novembre 2004 3:04 15

642

Au cur de Java 2 - Notions fondamentales

int getLineNumber()

Rcupre le numro de ligne du fichier source contenant le point dexcution de cet lment ou
1 si ces informations ne sont pas disponibles.

String getClassName()

Rcupre le nom complet de la classe contenant le point dexcution de cet lment.

String getMethodName()

Rcupre le nom de la mthode contenant le point dexcution de cet lment. Le nom dun
constructeur est <init>. Le nom dun initialiseur statique est <clinit>. Il est impossible de
faire la distinction entre des mthodes surcharges ayant le mme nom.

boolean isNativeMethod()

Renvoie true si le point dexcution de cet lment se trouve dans une mthode native.

String toString()

Renvoie une chane mise en forme contenant le nom de la classe et de la mthode, le nom du
fichier ainsi que le numro de ligne lorsquil est disponible.

Un dernier mot sur la gestion des erreurs et des exceptions de Java


LExemple 11.2 provoque dlibrment un certain nombre derreurs et rcupre diffrentes exceptions
(voir Figure 11.2).
Figure 11.2
Un programme gnrant
des exceptions.

Essayez-le. Cliquez sur les boutons et observez les exceptions qui sont lances.
Comme vous le savez, une erreur de programmation telle quun mauvais indice de tableau lance une
RuntimeException; une tentative douvrir un fichier inexistant dclenche une IOException.
Curieusement, les erreurs en virgule flottante telles que la division par 0,0 ou la racine carre de 1
ne gnrent pas dexceptions (elles gnrent des valeurs particulires telles que "infini" ou "pas un
nombre", que nous avons vues au Chapitre 3). La division par zro sur des entiers lance une exception
ArithmeticException.
Nous capturons les exceptions lances par les mthodes actionPerformed dans la mthode
fireActionPerformed des boutons radio et nous les affichons dans le champ texte. Cependant, la

Livre Java .book Page 643 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

643

mthode actionPerformed est dclare pour ne pas lancer des exceptions sous contrle. Ainsi, le
handler pour le bouton "No such file" doit capturer lexception IOException.
Si vous cliquez sur le bouton "Throw unknown", un objet UnknownError est lanc. Il ne sagit pas
dune sous-classe dException, aussi notre programme ne peut-il pas lattraper. Au lieu de cela,
linterface utilisateur imprime un message derreur et un traage de pile sur lcran.
Exemple 11.2 : ExceptTest.java
import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
java.io.*;

public class ExceptTest


{
public static void main(String[] args)
{
ExceptTestFrame frame = new ExceptTestFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
/**
Un cadre avec un panneau pour tester diverses exceptions
*/
class ExceptTestFrame extends JFrame
{
public ExceptTestFrame()
{
setTitle("ExceptTest");
ExceptTestPanel panel = new ExceptTestPanel();
add(panel);
pack();
}
}
/**
Un panneau avec des boutons radio pour lancer des
extraits de code et tudier leur comportement en cas
dexceptions
*/
class ExceptTestPanel extends Box
{
public ExceptTestPanel()
{
super(BoxLayout.Y_AXIS);
group = new ButtonGroup();
// ajouter des boutons radio pour les extraits de code
addRadioButton("Integer divide by zero", new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
a[1] = 1 / (a.length - a.length);

Livre Java .book Page 644 Jeudi, 25. novembre 2004 3:04 15

644

Au cur de Java 2 - Notions fondamentales

}
});
addRadioButton("Floating point divide by zero", new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
a[1] = a[2] / (a[3] - a[3]);
}
});
addRadioButton("Array bounds", new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
a[1] = a[10];
}
});
addRadioButton("Bad cast", new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
a = (double[])event.getSource();
}
});
addRadioButton("Null pointer", new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
event = null;
System.out.println(event.getSource());
}
});
addRadioButton("sqrt(-1)", new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
a[1] = Math.sqrt(-1);
}
});
addRadioButton("Overflow", new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
a[1] = 1000 * 1000 * 1000 * 1000;
int n = (int) a[1];
}
});

Livre Java .book Page 645 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

addRadioButton("No such file", new


ActionListener()
{
public void actionPerformed(ActionEvent event)
{
try
{
InputStream in = new FileInputStream("woozle.txt");
}
catch (IOException e)
{
textField.setText(e.toString());
}
}
});
addRadioButton("Throw unknown", new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
throw new UnknownError();
}
});
// ajouter le champ de texte pour laffichage de lexception
textField = new JTextField(30);
add(textField);
}
/**
Ajoute un bouton radio avec un couteur donn au
panneau. Intercepte toute exception dans la mthode
actionPerformed de lcouteur.
@param s Le libell du bouton radio
@param listener Lcouteur daction pour le bouton radio
*/
private void addRadioButton(String s, ActionListener listener)
{
JRadioButton button = new JRadioButton(s, false)
{
// le bouton appelle cette mthode pour dclencher
// lvnement daction. Nous la surchargeons pour intercepter
// les exceptions
protected void fireActionPerformed(ActionEvent event)
{
try
{
textField.setText("No exception");
super.fireActionPerformed(event);
}
catch (Exception e)
{
textField.setText(e.toString());
}
}
};
button.addActionListener(listener);

645

Livre Java .book Page 646 Jeudi, 25. novembre 2004 3:04 15

646

Au cur de Java 2 - Notions fondamentales

add(button);
group.add(button);
}
private ButtonGroup group;
private JTextField textField;
private double[] a = new double[10];
}

Quelques conseils sur lutilisation des exceptions


Les programmeurs ne sentendent pas tous sur lutilisation des exceptions. Certains considrent
toutes les exceptions sous contrle comme une vritable nuisance, dautres nen ont jamais assez.
Quant nous, nous considrons que les exceptions (mme celles sous contrle) ont un rle jouer ;
nous vous proposons donc quelques conseils pour les utiliser correctement.
1. La gestion des exceptions nest pas cense remplacer un test simple.
Pour illustrer cette affirmation, nous avons crit un code qui utilise la classe prdfinie Stack. Le
code de lExemple 11.3 tente un million de fois de dpiler une pile vide. Il commence par tester
si la pile est vide.
if (!s.empty()) s.pop();

Ensuite nous lui indiquons de dpiler quel que soit ltat de la pile et capturons EmptyStackException qui mentionne que nous naurions pas d le faire.
try()
{
s.pop();
}
catch(EmptyStackException e)
{
}

Sur notre ordinateur, le test nous donne les temps suivants (voir Tableau 11.1).
Tableau 11.1 : Dure des tests

Test

Throw/Catch

154 ms

10 739 ms

Nous voyons quil faut plus de temps pour intercepter une exception que pour effectuer un
simple test. La morale de lhistoire est : il ne faut employer les exceptions que dans des cas trs
particuliers.
2. Ne faites pas une gestion ultrafine des exceptions.
Bien souvent, on encapsule chaque instruction dans un bloc try distinct :
OutputStream out;
Stack s;
for (i = 0; i < 100; i++)
{

Livre Java .book Page 647 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

647

try
{
n = s.pop();
}
catch (EmptyStackException s)
{
// la pile tait vide
}
try
{
out.writeInt(n);
}
catch (IOException e)
{
// problme dcriture dans le fichier
}
}

Cette faon de procder pnalise normment votre programme. Il faut rflchir la tche que
doit accomplir le programme. Nous voulons ici dpiler 100 nombres et les crire dans un fichier
(ce nest quun exemple). Nous ne pouvons pas en ralit intervenir en cas de problme : si la
pile est vide, nous ny pouvons rien ; si une erreur apparat dans le fichier, elle ne disparatra pas
par magie. Il est par la suite raisonnable dencapsuler la tche tout entire dans un bloc try puisque,
si lune des deux oprations est impossible, nous ne pouvons que labandonner :
try
{
for (i = 0; i < 100; i++)
{
n = s.pop();
out.writeInt(n);
}
}
catch (IOException e)
{
// problme dcriture dans le fichier
}
catch (EmptyStackException s)
{
// pile vide
}

Ce programme est beaucoup plus adquat. Il satisfait parfaitement lun des devoirs du traitement
des exceptions, savoir sparer le traitement normal et le traitement derreur.
3. Faites bon usage de la hirarchie des exceptions.
Ne vous contentez pas de lancer une RuntimeException. Trouvez une sous-classe adapte ou
crez la vtre. Il ne faut pas simplement intercepter Throwable, car votre code sera alors plus
difficile lire et modifier. Respectez la diffrence entre les exceptions sous et hors contrle.
Les exceptions sous contrle sont invitablement lourdes grer (ne les dclenchez pas pour des
erreurs logiques). La bibliothque de rflexion, par exemple, se trompe ce sujet. Les appelants
doivent souvent intercepter des exceptions dont ils savent quelles ne peuvent jamais survenir.
Nhsitez pas transformer une exception en une autre qui soit mieux adapte. Par exemple,
lorsque vous analysez un entier dans un fichier, interceptez lexception NumberFormatException
et transformez-la en sous-classe de IOException ou MySubsystemException.

Livre Java .book Page 648 Jeudi, 25. novembre 2004 3:04 15

648

Au cur de Java 2 - Notions fondamentales

4. Ne muselez pas les exceptions.


En Java, cest une tentation irrsistible. Vous crivez, par exemple, une mthode qui en appelle
une autre susceptible de lancer une exception une fois par sicle. Le compilateur proteste parce
que vous navez pas dclar lexception dans la liste des throws de votre mthode, ce que vous
ne voulez pas faire parce que le compilateur va dtecter une erreur pour toutes les mthodes
appelant votre mthode. Aussi procdez-vous de la faon suivante :
public Image loadImage(String s)
{
try
{
code menaant de lancer des exceptions sous contrle
}
catch(Exception e)
{} // et ainsi
}

Votre programme se compilera maintenant sans problme ; il sexcutera parfaitement, sauf sil
survient une exception. Lexception sera passe sous silence ! Donc, si vous considrez que les
exceptions ont une certaine importance, il vous faut investir un peu dans leur traitement.
5. Lorsque vous dtectez une erreur, "lamour vache" vaut mieux que lindulgence.
Certains programmeurs sinquitent du lancement des exceptions lorsquils dtectent une erreur.
Il conviendrait peut-tre mieux de renvoyer une valeur factice plutt que de lancer une exception
lorsquune mthode est appele avec des paramtres non valides. Par exemple, Stack.pop
devrait-il renvoyer null plutt que lancer une exception lorsquune pile est vide ? Nous croyons
quil vaut mieux lancer une EmptyStackException au point dchec que laisser une NullPointerException survenir ultrieurement.
6. Il ne faut pas avoir honte de propager les exceptions.
Trs souvent, on se croit oblig de traiter toutes les exceptions rpertories. Supposons une
mthode susceptible de lancer une exception, par exemple le constructeur FileInputStream ou
la mthode readLine: dinstinct on va intercepter lexception ventuellement gnre, alors
quil est en ce cas prfrable de propager lexception.
public void readStuff(String name) throws IOException
// ne pas avoir honte!
{
FileInputStream in = new FileInputStream(name);
. . .
}

Les mthodes des niveaux suprieurs sont en gnral mieux prpares informer lutilisateur sur les
erreurs ou interrompre lexcution de commandes qui nont pas abouti.
INFO
Les rgles 5 et 6 peuvent tre rsumes dans la phrase suivante : "Dclencher dabord, intercepter plus tard."

Livre Java .book Page 649 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

649

Exemple 11.3 : ExceptionalTest.java


import java.util.*;
public class ExceptionalTest
{
public static void main(String[] args)
{
int i = 0;
int ntry = 1000000;
Stack s = new Stack();
long s1;
long s2;
// tester si la pile est vide ntry fois
System.out.println("Testing for empty stack");
s1 = new Date().getTime();
for (i = 0; i <= ntry; i++)
if (!s.empty()) s.pop();
s2 = new Date().getTime();
System.out.println((s2 - s1)+ " milliseconds");
// dpiler une pile vide ntry fois et intercepter
// lexception qui en rsulte
System.out.println("Catching EmptyStackException");
s1 = new Date().getTime();
for (i = 0; i <= ntry; i++)
{
try
{
s.pop();
}
catch(EmptyStackException e)
{
}
}
s2 = new Date().getTime();
System.out.println((s2 - s1)+ " milliseconds");
}
}

La consignation
Tous les programmeurs Java connaissent bien le processus dinsertion des appels System.out.println dans un code problmes, pour se faire une ide du comportement du programme.
Bien sr, lorsque vous aurez dcouvert la cause du problme, les instructions daffichage seffaceront ; malgr tout, elles rapparatront au prochain problme. LAPI de consignation du JDK 1.4 vise
surmonter ce problme. Ses principaux avantages sont les suivants :
m

Il est facile de supprimer tous les enregistrements du journal ou seulement ceux au-del dun
certain niveau, et il est aussi simple de les retrouver.

Les journaux supprims sont trs lgers, la pnalit est donc mince les conserver dans le code
de consignation de votre application.

Les enregistrements des journaux peuvent tre dirigs vers diffrents gestionnaires, afin dtre
affichs sur la console, stocks dans un fichier, etc.

Livre Java .book Page 650 Jeudi, 25. novembre 2004 3:04 15

650

Au cur de Java 2 - Notions fondamentales

Les systmes de consignation et de gestion sont en mesure de filtrer les enregistrements. Selon
les critres de leurs crateurs, les filtres rejettent les entres de journal inintressantes.

Les enregistrements de journaux peuvent tre mis en forme de diverses manires, par exemple en
texte brut ou en XML.

Les applications peuvent faire appel divers systmes de consignation, utilisant des noms hirarchiques comme com.mycompany.myapp, identiques aux noms de packages.

Par dfaut, la configuration de la consignation se contrle grce un fichier de configuration.


Les applications sont en mesure de remplacer ce mcanisme si on le souhaite.

Consignation de base
Commenons par le cas le plus simple qui soit. Le systme de consignation gre un enregistreur par
dfaut, nomm Logger.global, que vous pouvez utiliser la place de System.out. Sa mthode
info permet de consigner un message informatif :
Logger.global.info("File->Open menu item selected");

Par dfaut, lenregistrement saffiche comme suit :


May 10, 2004 10:12:15 PM LoggingImageViewer fileOpen
INFO: File->Open menu item selected

Vous aurez remarqu que lheure et les noms de la classe et de la mthode dappel apparaissent automatiquement. Toutefois, si vous appelez
Logger.global.setLevel(Level.OFF);

un endroit qui convient (par exemple au dbut de main), lensemble de la consignation est
supprim.

Consignation avance
Aprs cette introduction plus que simpliste, voyons quoi ressemble la consignation pour les professionnels. Dans une application professionnelle, il est inutile denregistrer toutes les entres dun
enregistreur global. Il vaut mieux dfinir quelques critres.
Lorsque vous appelez pour la premire fois un enregistreur ayant un nom donn, celui-ci se cre :
Logger myLogger = Logger.getLogger("com.mycompany.myapp");

Les appels ultrieurs ce nom produiront le mme objet logger.


A linstar des noms de packages, les noms denregistreurs affichent une structure hirarchique. Ils
sont en fait plus hirarchiques que les packages. En effet, il nexiste aucune relation smantique
entre un package et son parent, or les enfants et les parents des enregistreurs (logger) partagent
certaines proprits. Par exemple, si vous dfinissez le niveau de consignation sur lenregistreur
"com.mycompany", ses enfants hriteront de ce niveau.
Il existe sept niveaux de consignation :
m

SEVERE

WARNING

Livre Java .book Page 651 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

INFO

CONFIG

FINE

FINER

FINEST

Exceptions et mise au point

651

Par dfaut, les trois niveaux suprieurs sont consigns, mais vous pouvez modifier cela :
logger.setLevel(Level.FINE);

Dsormais, la consignation concerne tous les niveaux partir de FINE.


Vous pouvez galement utiliser Level.ALL pour consigner tous les niveaux ou Level.OFF pour
annuler toute consignation.
Il existe certaines mthodes de consignation pour tous les niveaux, comme :
logger.warning(message);
logger.fine(message);

etc. Mais vous pouvez aussi utiliser la mthode log, en indiquant le niveau, comme suit :
logger.log(Level.FINE, message);

ASTUCE
La configuration par dfaut permet de consigner tous les enregistrements ayant le niveau INFO ou suprieur. Vous
devez donc utiliser les niveaux CONFIG, FINE, FINER et FINEST pour dboguer les messages insignifiants pour
lutilisateur du programme et pourtant utiles au diagnostic.

ATTENTION
Si vous rglez le niveau de consignation sur une valeur plus prcise que INFO, modifiez galement la configuration
du gestionnaire de lenregistreur. Ce gestionnaire par dfaut supprime les messages infrieurs au niveau INFO.
Consultez la section suivante pour en savoir plus.

Lenregistrement par dfaut prcise le nom de la classe et de la mthode qui contient lappel de
consignation, comme cela est suggr par la pile dappel. Malgr tout, si la machine virtuelle
optimise lexcution, aucune information prcise ne sera disponible. Vous pouvez alors utiliser la
mthode logp pour indiquer lemplacement prcis de la classe et de la mthode dappel. La signature de mthode est la suivante :
void logp(Level l, String className, String methodName, String message)

Il existe certaines mthodes pratiques pour retracer le flux de lexcution :


void
void
void
void
void

entering(String className, String methodName)


entering(String className, String methodName, Object param)
entering(String className, String methodName, Object[] params)
exiting(String className, String methodName)
exiting(String className, String methodName, Object result)

Livre Java .book Page 652 Jeudi, 25. novembre 2004 3:04 15

652

Au cur de Java 2 - Notions fondamentales

Par exemple :
int read(String file, String pattern)
{
logger.entering("com.mycompany.mylib.Reader", "read",
new Object[] { file, pattern });
. . .
logger.exiting("com.mycompany.mylib.Reader", "read", count);
return count;
}

Ces appels gnrent des enregistrements de journaux du niveau FINER, qui commencent par les chanes
ENTRY et RETURN.
INFO
A lavenir, les mthodes de consignation seront rcrites pour supporter les listes de paramtres variables
("varargs"). Il sera alors possible de raliser des appels tels que logger.entering("com.mycompany.mylib.Reader", "read", file, pattern).

Les journaux sont souvent utiliss pour enregistrer les exceptions inattendues. Deux mthodes
incluent une description de lexception dans lenregistrement :
void throwing(String className, String methodName, Throwable t)
void log(Level 1, String message, Throwable t)

Les usages courants sont les suivants :


if (. . .)
{
IOException exception = new IOException(". . .");
logger.throwing("com.mycompany.mylib.Reader", "read", exception);
throw exception;
}

et
try
{
. . .
}
catch (IOException e)
{
Logger.getLogger("com.mycompany.myapp").log(
Level.WARNING, "Reading image", e);
}

Lappel throwing consigne un enregistrement du niveau FINER et un message commenant par


THROW.

Modifier la configuration du gestionnaire de journaux


Diverses proprits du systme de consignation peuvent tre modifies par un changement apport
au fichier de configuration. Le fichier par dfaut est situ
jre/lib/logging.properties

Livre Java .book Page 653 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

653

Pour utiliser un autre fichier, faites passer la proprit java.util.logging.config.file sur


lemplacement du fichier, en lanant votre application avec
java -Djava.util.logging.config.file= FichierConfig ClasseMain

ATTENTION
Appeler System.setProperty("java.util.logging.config.file", file) dans main nest daucun
effet car le gestionnaire de journaux est initialis au dmarrage de la machine virtuelle, avant mme lexcution
de main.

Pour changer le niveau de consignation par dfaut, modifiez la ligne suivante dans le fichier de configuration :
.level=INFO

Vous pouvez spcifier les niveaux de consignation de vos propres enregistreurs en ajoutant des lignes
telles que
com.mycompany.myapp.level=FINE

Il sagit ici dajouter le suffixe .level au nom de lenregistreur.


Comme vous le verrez plus loin dans cette section, ce ne sont pas rellement les enregistreurs qui
envoient un message la console, cest plutt le rle des gestionnaires. Ceux-ci possdent aussi des
niveaux. Pour afficher les messages FINE sur la console, vous devez aussi dfinir
java.util.logging.ConsoleHandler.level=FINE

ATTENTION
Les paramtres de configuration du gestionnaire de journaux ne sont pas des proprits systme. Lancer un
programme par -Dcom.mycompany.myapp.level=FINE na aucune influence sur lenregistreur.

INFO
Le fichier des proprits de consignation est trait par la classe java.util.logging.LogManager. Vous pouvez choisir un autre gestionnaire de journal en dfinissant la proprit systme java.util.logging.manager sur le nom
dune sous-classe. Vous pouvez aussi conserver le gestionnaire de journaux standard et pourtant contourner
linitialisation partir du fichier de proprit de consignation. Dfinissez la proprit systme
java.util.logging.config.class sur le nom dune classe qui dfinit autrement les proprits du gestionnaire de journal. Consultez la documentation API pour en savoir plus sur la classe LogManager.

La localisation
Il est quelquefois ncessaire de localiser les messages de consignation de sorte quils puissent
tre lus par des utilisateurs parlant dautres langues. Linternationalisation des applications sera
traite au Chapitre 10 du Volume 2. Voici toutefois quelques points garder en tte pour localiser
les messages de consignation.
Les applications localises contiennent des informations qui sont fonction des paramtres rgionaux, contenus dans des groupes de ressources. Un groupe de ressources est constitu dun jeu

Livre Java .book Page 654 Jeudi, 25. novembre 2004 3:04 15

654

Au cur de Java 2 - Notions fondamentales

de correspondances (par exemple Etats-Unis ou Allemagne). A titre dexemple, un groupe de


ressources peut mettre en correspondance la chane "readingFile" avec les chanes "Reading file"
en anglais ou "Achtung ! Datei wird eingelesen" en allemand.
Un programme peut contenir plusieurs groupes de ressources : un pour les menus, un autre pour les
messages de consignation, etc. Chaque groupe de ressources possde un nom (par exemple
"com.mycompany.logmessages"). Pour ajouter des correspondances un groupe de ressources,
fournissez un fichier chaque paramtre rgional. Les correspondances des messages anglais se
trouvent dans le fichier com/mycompany/logmessages_en.properties et les correspondances de
messages allemands, dans com/mycompany/logmessages_de.properties (les codes de, en dsignent les langues). Regroupez ces fichiers dans les fichiers de classe de votre application, de sorte
que la classe ResourceBundle les retrouve immdiatement. Ces fichiers sont en texte brut et
contiennent des entres telles que
readingFile=Achtung! Datei wird eingelesen
renamingFile=Datei wird umbenannt
...

Lorsque vous demandez un enregistreur, vous pouvez spcifier un groupe de ressources :


Logger logger = Logger.getLogger(loggerName,
"com.mycompany.logmessages");

Vous spcifiez ensuite la cl du groupe de ressources, et non la chane du message :


logger.info("readingFile");

Il faut souvent inclure des arguments dans les messages localiss. Dans ce cas, le message doit
contenir des caractres demplacement {0}, {1}, etc. Par exemple, pour inclure le nom du fichier
avec un message journal, insrez le caractre demplacement comme ceci :
Reading file {0}.
Achtung! Datei {0} wird eingelesen.

Vous transfrez ensuite les valeurs au caractre demplacement en appelant lune des mthodes
suivantes :
logger.log(Level.INFO, "readingFile", fileName);
logger.log(Level.INFO, "renamingFile", new Object[] {
oldName, newName });

Les gestionnaires
Par dfaut, les enregistreurs envoient leurs enregistrements un ConsoleHandler qui les affiche
dans le flux System.err. Plus prcisment, lenregistreur envoie lenregistrement au gestionnaire
parent, et le dernier anctre (avec pour nom "") possde un ConsoleHandler.
De mme, les gestionnaires disposent dun niveau de consignation. Pour quun enregistrement soit
consign, son niveau de consignation doit tre suprieur au seuil de lenregistreur et celui du
gestionnaire. Le fichier de configuration du gestionnaire de consignation dfinit le niveau de consignation du gestionnaire de console par dfaut comme suit :
java.util.logging.ConsoleHandler.level=INFO

Livre Java .book Page 655 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

655

Les enregistrements du journal ayant le niveau FINE modifient la fois le niveau de lenregistreur
par dfaut et celui du gestionnaire dans la configuration. Vous pouvez aussi contourner le fichier de
configuration et installer votre propre gestionnaire :
Logger logger = Logger.getLogger("com.mycompany.myapp");
logger.setLevel(Level.FINE);
logger.setUseParentHandlers(false);
Handler handler = new ConsoleHandler();
handler.setLevel(Level.FINE);
logger.addHandler(handler);

Par dfaut, un enregistreur enregistre dans ses propres gestionnaires et dans ceux du parent. Notre
enregistreur est un enfant du principal enregistreur (avec le nom ""), qui envoie la console tous les
enregistrements ayant le niveau INFO ou suprieur. Mais ces enregistrements ne doivent pas apparatre
deux fois. Nous dfinissons donc la proprit useParentHandlers sur false.
Pour envoyer des enregistrements de journal partout ailleurs, ajoutez un autre gestionnaire. LAPI de
consignation fournit deux gestionnaires qui se montreront fort utiles cet effet, un FileHandler et
un SocketHandler. SocketHandler envoie les enregistrements vers un hte et un port spcifis.
FileHandler est bien plus intressant puisquil collecte des enregistrements dans un fichier.
Vous pouvez envoyer les enregistrements vers un gestionnaire de fichiers par dfaut, comme ceci :
FileHandler handler = new FileHandler();
logger.addHandler(handler);

Les enregistrements sont envoys vers un fichier javan.log dans le rpertoire de base de lutilisateur, o n est remplac par un nombre pour rendre le fichier unique. Lorsquun systme ne connat
pas le rpertoire de base de lutilisateur (par exemple sous Windows 95/98/Me), le fichier est stock
dans un emplacement par dfaut comme C:\Windows. Par dfaut, les enregistrements sont mis en
forme au format XML. Un enregistrement de journal ordinaire a la forme suivante :
<record>
<date>2002-02-04T07:45:15</date>
<millis>1012837515710</millis>
<sequence>1</sequence>
<logger>com.mycompany.myapp</logger>
<level>INFO</level>
<class>com.mycompany.mylib.Reader</class>
<method>read</method>
<thread>10</thread>
<message>Reading file corejava.gif</message>
</record>

Vous pouvez modifier le comportement par dfaut du gestionnaire de fichiers en changeant ses paramtres de configuration (voir Tableau 11.2) ou en utilisant un autre constructeur (voir les notes API
en fin de section).
Il est souvent prfrable de changer galement le nom par dfaut du fichier journal. Vous pourrez
utiliser un modle diffrent, par exemple %h/myapp.log (voir Tableau 11.3 pour obtenir une explication des variables des modles).
Lorsque plusieurs applications (ou plusieurs copies dune mme application) utilisent le mme
fichier journal, il est recommand dactiver la balise "append". Vous pouvez galement insrer %u
dans le modle du nom de fichier, pour que chaque application cre sa propre copie du journal.

Livre Java .book Page 656 Jeudi, 25. novembre 2004 3:04 15

656

Au cur de Java 2 - Notions fondamentales

Il est galement conseill dactiver la rotation des fichiers. Les fichiers journaux sont conservs
suivant un modle squentiel tel que myapp.log.0, myapp.log.1, myapp.log.2, etc. Ds quun fichier
dpasse le plafond dtermin, le plus vieux est supprim, les autres sont renomms et un nouveau
fichier est cr avec le numro 0.
Tableau 11.2 : Paramtres de configuration du gestionnaire de fichiers

Proprit de configuration

Description

Paramtre par dfaut

java.util.logging.
FileHandler.level

Le niveau du gestionnaire.

Level.ALL

java.util.logging.
FileHandler.append

Contrle si le gestionnaire doit


continuer un fichier existant ou
ouvrir un nouveau fichier pour
chaque programme.

false

java.util.logging.
FileHandler.limit

Le nombre maximal approximatif


doctets crire dans un fichier
avant den ouvrir un autre (0 = pas
de limite).

0 (pas de limite) dans la


classe FileHandler, 50000
dans la configuration par
dfaut du gestionnaire de
journaux

java.util.logging.
FileHandler.pattern

Le modle de nom pour le fichier


journal. Voir Tableau 11.3 pour
connatre ses variables.

%h/java%u.log

java.util.logging.
FileHandler.count

Le nombre de journaux dans une


suite.

1 (pas de rotation)

java.util.logging.
FileHandler.filter

La classe du filtre utiliser.

Pas de filtrage

java.util.logging.
FileHandler.encoding

Le codage de caractres utiliser.

Le codage de la plate-forme

java.util.logging.
FileHandler.formatter

Le formateur des enregistrements.

java.util.logging.XMLFormatter

ASTUCE
De nombreux programmeurs utilisent la consignation pour aider le personnel du support technique. En cas de drglement dun programme au cours dune utilisation, lutilisateur peut renvoyer les fichiers journaux pour analyse.
Dans ce cas, il convient dactiver la balise "append", dutiliser la rotation des fichiers, voire de faire les deux.

Vous pouvez galement dfinir vos propres gestionnaires en prolongeant la classe Handler ou
StreamHandler. Nous dfinissons ce gestionnaire dans le programme dexemple la fin de cette
section. Il affiche les enregistrements dans une fentre (voir Figure 11.3).

Livre Java .book Page 657 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

657

Tableau 11.3 : Variables du modle de fichier journal

Variable

Description

%h

La valeur de la proprit systme user.home

%t

Le rpertoire temporaire du systme

%u

Un numro unique pour viter les conflits

%g

Le numro pour les journaux par rotation (le suffixe .%g indique que la rotation est
spcifie, le modle ne contient pas %g)

%%

Le caractre %

Le gestionnaire tend la classe StreamHandler et installe un flux dont les mthodes write affichent
la sortie dans une zone de texte :
class WindowHandler extends StreamHandler
{
public WindowHandler()
{
. . .
final JTextArea output = new JTextArea();
setOutputStream(new
OutputStream()
{
public void write(int b) {} // not called
public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
}
. . .
}

Figure 11.3
Un gestionnaire
de journal affichant des
enregistrements dans
une fentre.

Cette approche prsente tout de mme un problme : le gestionnaire met les enregistrements en
mmoire tampon et ne les crit sur le flux que lorsque le tampon est plein. Nous allons donc craser
la mthode publish pour vider le tampon aprs chaque enregistrement :
class WindowHandler extends StreamHandler
{

Livre Java .book Page 658 Jeudi, 25. novembre 2004 3:04 15

658

Au cur de Java 2 - Notions fondamentales

. . .
public void publish(LogRecord record)
{
super.publish(record);
flush();
}
}

Pour obtenir des gestionnaires un peu plus exotiques, tendez la classe Handler et dfinissez les
mthodes publish, flush et close.

Les filtres
Par dfaut, les enregistrements sont filtrs en fonction de leurs niveaux de consignation. Chaque
enregistreur et chaque gestionnaire peuvent avoir un filtre optionnel qui ralisera un filtrage complmentaire. Pour dfinir un filtre, implmentez linterface Filter et dfinissez la mthode
boolean isLoggable(LogRecord record)

Analysez lenregistrement du journal laide des critres souhaits et renvoyez true pour ceux que
vous voulez inclure dans le journal. Par exemple, un filtre particulier peut ntre intress que par les
messages gnrs par les mthodes entering et exiting. Le filtre doit alors appeler
record.getMessage() et vrifier sil commence par ENTRY ou RETURN.
Pour installer un filtre dans un enregistreur ou un gestionnaire, appelez simplement la mthode
setFilter. Sachez que vous pouvez installer plusieurs filtres la fois.

Les formateurs
Les classes ConsoleHandler et FileHandler dlivrent les enregistrements des journaux aux
formats texte et XML. Mais vous pouvez aussi dfinir vos propres formats. Pour cela, prolongez la
classe Formatter et surchargez la mthode
String format(LogRecord record)

Vous pouvez mettre en forme les informations contenues dans lenregistrement selon vos prfrences et renvoyer la chane de rsultat. Dans votre mthode format, vous pouvez appeler la mthode
String formatMessage(LogRecord record)

Cette mthode met en forme le message qui fait partie de lenregistrement, en remplaant les paramtres
et en appliquant la localisation.
De nombreux formats de fichiers (comme XML) exigent un bloc de dbut et un bloc de fin pour
entourer les enregistrements mis en forme. Dans ce cas, surchargez les mthodes
String getHead(Handler h)
String getTail(Handler h)

Enfin, appelez la mthode setFormatter pour installer le formateur dans le gestionnaire.


Convenons quavec autant doptions il est facile de se perdre quand il sagit de la consignation.
Lencadr "ABC de la consignation" rsume les oprations les plus communes.

Livre Java .book Page 659 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

659

ABC de la consignation
1. Pour une application simple, optez pour un enregistreur unique. Donnez-lui le nom de votre
package dapplication principale, comme com.mycompany.myprog. Vous pouvez toujours
rcuprer lenregistreur en appelant
Logger logger = Logger.getLogger("com.mycompany.myprog");
Pour plus de commodit, vous pourrez ajouter les champs statiques
private static final Logger logger = Logger.getLogger("com.mycompany.myprog");
aux classes ayant une grande activit de consignation.
2. La configuration par dfaut enregistre dans la console tous les messages du niveau INFO ou
suprieur. Choisissez un paramtre plus raisonnable pour votre application.
Le code suivant vrifie que tous les messages soient consigns dans un fichier spcifique
lapplication. Placez le code dans la mthode main de votre application :
if (System.getProperty("java.util.logging.config.class") == null
&& System.getProperty("java.util.logging.config.file") == null)
{
try
{
Logger.getLogger("").setLevel(Level.ALL);
final int LOG_ROTATION_COUNT = 10;
Handler handler = new FileHandler("%h/myapp.log", 0,
LOG_ROTATION_COUNT);
Logger.getLogger("").addHandler(handler);
}
catch (IOException e)
{
logger.log(Level.SEVERE, "Cant create log file handler", e);
}
}
3. Vous tes maintenant prt consigner le contenu qui vous intresse. Noubliez pas que tous
les messages ayant le niveau INFO, WARNING et SEVERE safficheront dans la console. Rservez
donc ces niveaux aux messages signifiants pour les utilisateurs du programme. Le niveau
FINE convient bien aux messages destins aux programmeurs.
Ds que vous tes tent dappeler System.out.println, mettez plutt un message journal :
logger.fine("File open dialog cancelled");
Vous devriez aussi consigner les exceptions inattendues, par exemple :
try
{
. . .
}
catch (SomeException e)
{
logger.log(Level.FINE, "explanation", e);
}

Livre Java .book Page 660 Jeudi, 25. novembre 2004 3:04 15

660

Au cur de Java 2 - Notions fondamentales

LExemple 11.4 prsente le code du visualisateur dimages qui consigne des vnements dans une
fentre journal.
Exemple 11.4 : LoggingImageViewer.java
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.awt.image.*;
java.io.*;
java.util.logging.*;
javax.swing.*;

/**
Une modification du code du visualisateur dimages qui consigne
divers vnements.
*/
public class LoggingImageViewer
{
public static void main(String[] args)
{
if (System.getProperty("java.util.logging.config.class") == null
&& System.getProperty("java.util.logging.config.file") == null)
{
try
{
Logger.getLogger("").setLevel(Level.ALL);
final int LOG_ROTATION_COUNT = 10;
Handler handler = new FileHandler(
"%h/LoggingImageViewer.log", 0, LOG_ROTATION_COUNT);
Logger.getLogger("").addHandler(handler);
}
catch (IOException e)
{
Logger.getLogger("com.horstmann.corejava").log(Level.SEVERE,
"Cant create log file handler", e);
}
}
Handler windowHandler = new WindowHandler();
windowHandler.setLevel(Level.ALL);
Logger.getLogger("com.horstmann.corejava").
addHandler(windowHandler);
JFrame frame = new ImageViewerFrame();
frame.setTitle("LoggingImageViewer");
frame.setSize(300, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Logger.getLogger("com.horstmann.corejava").fine("Showing frame");
frame.setVisible(true);
}
}
/**
Le cadre qui affiche limage.
*/
class ImageViewerFrame extends JFrame

Livre Java .book Page 661 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

{
public ImageViewerFrame()
{
logger.entering("ImageViewerFrame", "<init>");
// configurer la barre de menus
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem openItem = new JMenuItem("Open");
menu.add(openItem);
openItem.addActionListener(new FileOpenListener());
JMenuItem exitItem = new JMenuItem("Exit");
menu.add(exitItem);
exitItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
logger.fine("Exiting.");
System.exit(0);
}
});
// utiliser un intitul pour afficher les images
label = new JLabel();
add(label);
logger.exiting("ImageViewerFrame", "<init>");
}
private class FileOpenListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
logger.entering("ImageViewerFrame.FileOpenListener",
"actionPerformed", event);
// configurer le slecteur de fichiers
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
// accepter tous les fichiers se terminant par .gif
chooser.setFileFilter(new
javax.swing.filechooser.FileFilter()
{
public boolean accept(File f)
{
return f.getName().toLowerCase().endsWith(".gif") ||
f.isDirectory();
}
public String getDescription()
{
return "GIF Images";
}
});

661

Livre Java .book Page 662 Jeudi, 25. novembre 2004 3:04 15

662

Au cur de Java 2 - Notions fondamentales

// afficher la bote de dialogue du slecteur de fichiers


int r = chooser.showOpenDialog(ImageViewerFrame.this);
// si le fichier image est accept, le dfinir comme
// icne de lintitul
if (r == JFileChooser.APPROVE_OPTION)
{
String name = chooser.getSelectedFile().getPath();
logger.log(Level.FINE, "Reading file {0}", name);
label.setIcon(new ImageIcon(name));
}
else
logger.fine("File open dialog canceled.");
logger.exiting("ImageViewerFrame.FileOpenListener",
"actionPerformed");
}
}
private JLabel label;
private static Logger logger =
Logger.getLogger("com.horstmann.corejava");
}
/**
Un gestionnaire pour afficher les enregistrements de journal
dans une fentre.
*/
class WindowHandler extends StreamHandler
{
public WindowHandler()
{
frame = new JFrame();
final JTextArea output = new JTextArea();
output.setEditable(false);
frame.setSize(200, 200);
frame.add(new JScrollPane(output));
frame.setFocusableWindowState(false);
frame.setVisible(true);
setOutputStream(new
OutputStream()
{
public void write(int b) {} // not called
public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
}
public void publish(LogRecord record)
{
if (!frame.isVisible()) return;
super.publish(record);
flush();
}
private JFrame frame;
}

Livre Java .book Page 663 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

663

java.util.logging.Logger 1.4
Logger getLogger(String loggerName)
Logger getLogger(String loggerName, String bundleName)

Rcuprent lenregistreur ayant le nom donn. Sil nexiste pas, il est cr.
Paramtres :

void
void
void
void
void
void
void

loggerName

Le nom hirarchique de lenregistreur,


comme com.mycompany.myapp.

bundleName

Le nom du groupe de ressources permettant de rechercher


les messages localiss.

severe(String message)
warning(String message)
info(String message)
config(String message)
fine(String message)
finer(String message)
finest(String message)

Consignent un enregistrement avec le niveau indiqu par le nom de la mthode avec le message
donn.

void
void
void
void
void

entering(String className, String methodName)


entering(String className, String methodName, Object param)
entering(String className, String methodName, Object[] param)
exiting(String className, String methodName)
exiting(String className, String methodName, Object result)

Consignent un enregistrement dcrivant lentre ou la sortie dune mthode avec le ou les paramtres donns ou la valeur de retour.

void throwing(String className, String methodName, Throwable t)

Consigne un enregistrement dcrivant le lancement de lobjet exception donn.

void
void
void
void

log(Level
log(Level
log(Level
log(Level

level,
level,
level,
level,

String
String
String
String

message)
message, Object obj)
message, Object[] objs)
message, Throwable t)

Consignent un enregistrement avec le niveau donn et le message, en incluant en option des


objets ou un throwable. Pour inclure des objets, le message doit contenir des emplacements de
mise en forme tels {0}, {1}, etc.

void logp(Level
void logp(Level
Object obj)
void logp(Level
Object[] objs)
void logp(Level
Throwable t)

level, String className, String methodName, String message)


level, String className, String methodName, String message,
level, String className, String methodName, String message,
level, String className, String methodName, String message,

Consignent un enregistrement avec le niveau donn, des informations prcises sur lappelant et
un message. Incluent, en option, des objets ou un throwable.

void logrb(Level level, String className, String methodName, String bundleName,


String message)
void logrb(Level level, String className, String methodName, String bundleName,
String message, Object obj)

Livre Java .book Page 664 Jeudi, 25. novembre 2004 3:04 15

664

Au cur de Java 2 - Notions fondamentales

void logrb(Level level, String className, String methodName, String bundleName,


String message, Object[] objs)

void logrb(Level level, String className, String methodName, String bundleName,


String message, Throwable t)

Consignent un enregistrement avec le niveau donn, des informations prcises sur lappelant, le
nom du groupe de ressources et le message. Consignent, en option, des objets ou un throwable.

Level getLevel()
void setLevel(Level l)

Rcuprent ou dfinissent le niveau de cet enregistreur.

Logger getParent()
void setParent(Logger l)

Rcuprent ou dfinissent lenregistreur parent de cet enregistreur.

Handler[] getHandlers()

Rcupre tous les gestionnaires de cet enregistreur.

void addHandler(Handler h)
void removeHandler(Handler h)

Ajoutent ou suppriment un gestionnaire pour cet enregistreur.

boolean getUseParentHandlers()
void setUseParentHandlers(boolean b)

Rcuprent et dfinissent la proprit "use parent handler" (utiliser le gestionnaire parent). Si


cette proprit vaut true, lenregistreur transmet tous les enregistrements consigns aux gestionnaires de son parent.

Filter getFilter()
void setFilter(Filter f)

Rcuprent et dfinissent le filtre de cet enregistreur.


java.util.logging.Handler 1.4

abstract void publish(LogRecord record)

Envoie lenregistrement la destination prvue.

abstract void flush()

Efface toute donne mise en tampon.

abstract void close()

Efface toute donne mise en tampon et libre les ressources associes.

Filter getFilter()
void setFilter(Filter f)

Rcuprent et dfinissent le filtre de ce gestionnaire.

Formatter getFormatter()
void setFormatter(Formatter f)

Rcuprent et dfinissent le formateur de ce gestionnaire.

Level getLevel()
void setLevel(Level l)

Rcuprent et dfinissent le niveau de ce gestionnaire.

Livre Java .book Page 665 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

665

java.util.logging.ConsoleHandler 1.4

ConsoleHandler()

Construit un nouveau gestionnaire de console.


java.util.logging.FileHandler 1.4

FileHandler(String pattern)
FileHandler(String pattern, boolean append)
FileHandler(String pattern, int limit, int count)
FileHandler(String pattern, int limit, int count, boolean append)

Construisent un gestionnaire de fichiers.


Paramtres :

pattern

Le motif pour construire le nom du fichier journal.


Voir le Tableau 11.3 pour connatre les variables du modle.

limit

Le nombre maximal approximatif doctets avant quun


nouveau fichier journal ne soit ouvert.

count

Le nombre de fichiers dans une rotation.

append

Vaut true lorsquun gestionnaire de fichiers nouvellement


construit doit continuer un fichier journal existant.

java.util.logging.LogRecord 1.4

Level getLevel()

Rcupre le niveau de consignation de cet enregistrement.

String getLoggerName()

Rcupre le nom de lenregistreur qui consigne cet enregistrement.

ResourceBundle getResourceBundle()
String getResourceBundleName()

Rcuprent le groupe de ressources ou son nom pour lutiliser pour localiser le message ou null
si aucun nest fourni.

String getMessage()

Rcupre le message "brut" avant la localisation ou la mise en forme.

Object[] getParameters()

Rcupre les objets paramtre ou null si aucun nest fourni.

Throwable getThrown()

Rcupre lobjet lanc null si aucun nest fourni.

String getSourceClassName()
String getSourceMethodName()

Rcuprent lemplacement du code qui a consign cet enregistrement. Ces informations peuvent
tre fournies par le code de consignation ou automatiquement tires de la pile dexcution. Ceci
peut tre inexact si le code de consignation a fourni la mauvaise valeur ou si le code dexcution
tait optimis et que lemplacement exact ne peut pas tre extrait.

long getMillis()

Rcupre lheure de la cration, en millimes de seconde, depuis 1970.

Livre Java .book Page 666 Jeudi, 25. novembre 2004 3:04 15

666

Au cur de Java 2 - Notions fondamentales

long getSequenceNumber()

Rcupre le numro unique (dans la suite) de cet enregistrement.

int getThreadID()

Rcupre lID unique pour le thread dans lequel cet enregistrement a t cr. Ces ID sont attribus
par la classe LogRecord et nont pas de relation avec les autres ID de thread.
java.util.logging.Filter 1.4

boolean isLoggable(LogRecord record)

Renvoie true si lenregistrement de journal donn doit tre consign.


java.util.logging.Formatter 1.4

abstract String format(LogRecord record)

Renvoie la chane ne de la mise en forme de lenregistrement du journal.

String getHead(Handler h)
String getTail(Handler h)

Renvoient les chanes qui doivent apparatre au dbut et la fin du document contenant les enregistrements du journal. La superclasse Formatter dfinit ces mthodes de sorte quelles
renvoient la chane vide ; surchargez-les si ncessaire.

String formatMessage(LogRecord record)

Renvoie la partie du message qui a t localise et mise en forme.

Les assertions
Les assertions sont une langue souvent utilise pour une programmation dite "dfensive". Vous
pouvez tre convaincu quune proprit donne est remplie et vous reposer sur elle dans votre code.
Vous pourriez par exemple calculer :
double y = Math.sqrt(x);

Vous tes certain que x nest pas ngatif. Cest peut-tre le rsultat dun autre calcul qui ne peut pas
avoir de rsultat ngatif ou un paramtre dune mthode qui exige de ses appelants quils fournissent
uniquement des entres positives. Vous souhaitez pourtant procder une deuxime vrification
plutt que voir apparatre des valeurs droutantes virgule flottante dans votre calcul. Vous pourriez
bien sr lancer une exception :
if (x < 0) throw new IllegalArgumentException("x < 0");

Ce code, toutefois, reste dans le programme, mme une fois le test termin. Si vous avez de
nombreuses vrifications de ce type, le programme tourne un peu moins vite quil ne devrait.
Le mcanisme des assertions permet dinsrer des vrifications au moment du test, puis de les faire
disparatre automatiquement dans le code de production.
Depuis le JDK 1.4, le langage Java possde le mot cl assert. Il prend deux formes :
assert condition;

et
assert condition: expression;

Livre Java .book Page 667 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

667

Ces deux instructions valuent la condition et lancent une AssertionError si elle vaut false. Dans
la deuxime, lexpression est transmise au constructeur de lobjet AssertionError et transforme
en une chane de message.
INFO
Le seul objectif de la partie expression est de produire une chane de message. Lobjet AssertionError ne stocke
pas la valeur relle de lexpression, vous ne pouvez donc pas effectuer une requte dessus par la suite. Comme lindique la documentation du JDK avec son charme tout paternaliste, ceci "encouragerait les programmeurs tenter de
rcuprer aprs une panne dassertion, ce qui est contraire lobjectif de cette structure".

Pour sassurer que x nest pas ngatif, vous pouvez simplement utiliser linstruction
assert x >=0;

Vous pouvez aussi simplement transfrer la valeur relle de x dans lobjet AssertionError, de sorte
quil saffiche par la suite :
assert x >=0: x;

INFO C++
La macro assert du langage C transforme la condition dassertion en une chane qui saffiche si lassertion choue.
Par exemple, si assert(x >=0) choue, elle affiche que "x >=0" est la condition dfaillante. En Java, la condition
ne fait pas automatiquement partie du rapport derreur. Pour la voir, il faut la transfrer sous forme de chane dans
lobjet AssertionError: assert x >=0: "x >= 0".

Si vous utilisez le JDK 1.4, vous devez indiquer au compilateur que vous utilisez le mot cl assert.
Utilisez loption -source 1.4, comme ceci :
javac -source 1.4 MyClass.java

Depuis le JDK 5.0, la prise en charge des assertions est active par dfaut.

Activation et dsactivation des assertions


Les assertions sont dsactives par dfaut. Pour les activer, excutez le programme avec loption
-enableassert ou -ea:
java -enableassertions MyApp

Sachez quil est inutile de recompiler votre programme pour activer ou dsactiver les assertions.
Lactivation ou la dsactivation des assertions est une fonction du chargeur de classe. Lorsque les
assertions sont dsactives, le chargeur de classe retire le code de lassertion de sorte quil ne ralentisse pas lexcution.
Vous pouvez mme activer les assertions dans des classes spcifiques ou dans des packages
complets. Par exemple :
java -ea:MyClass -ea:com.mycompany.mylib... MyApp

Livre Java .book Page 668 Jeudi, 25. novembre 2004 3:04 15

668

Au cur de Java 2 - Notions fondamentales

Cette commande active les assertions pour la classe MyClass et toutes les classes du package
com.mycompany.mylib et ses sous-packages. Loption -ea active les assertions dans toutes les
classes du package par dfaut.
Vous pouvez aussi dsactiver les assertions dans certains packages et classes avec loption
-disableassertions ou -da:
java -ea:... -da:MyClass MyApp

Certaines classes sont charges non pas par un chargeur de classes mais directement par la machine
virtuelle. Vous pouvez utiliser ces commutateurs pour activer ou dsactiver au choix les assertions
dans ces classes. Toutefois, les arguments -ea et -da qui activent ou dsactivent toutes les assertions ne
sappliquent pas aux "classes systme" en labsence de chargeurs de classes. Utilisez largument
-enablesystemassertions/-esa pour activer les assertions dans les classes systme.
Il est aussi possible de contrler, par le biais de la programmation, le statut des assertions des chargeurs
de classes. Reportez-vous aux notes API de cette section.

Conseils dutilisation des assertions


Le langage Java propose trois mcanismes pour grer les pannes systme :
m

dclenchement dune exception ;

consignation ;

utilisation des assertions.

A quel moment choisir ces assertions ? Il faut se souvenir de ceci :


m

Les pannes dassertions sont supposes tre des erreurs dfinitives et irrcuprables.

Les vrifications dassertions ne sont actives que lors du dveloppement et du test (ceci est
quelquefois compar la situation suivante : porter un gilet de sauvetage lorsque lon se trouve
sur la rive et sen dbarrasser au milieu de locan).

Il est donc dconseill dutiliser les assertions pour signaler des conditions rcuprables une autre
partie du programme ou pour communiquer des problmes lutilisateur. Les assertions ne doivent
tre utilises que pour retrouver des erreurs internes au programme lors du test.
Etudions un scnario habituel : la vrification des paramtres de mthode. Faut-il utiliser les assertions pour rechercher des valeurs dindice interdites ou des rfrences null? Pour rpondre cette
question, tudiez la documentation de la mthode. Voyons par exemple la mthode Arrays.sort de
la bibliothque standard :
/**
Trie la gamme spcifie du tableau indiqu dans
lordre numrique croissant. La plage trier stend
de fromIndex compris toIndex, exclu.
@param a Le tableau trier.
@param fromIndex Lindice du premier lment (inclus) trier.
@param toIndex Lindice du dernier lment (exclu) trier.
@throws IllegalArgumentException si fromIndex > toIndex
@throws ArrayIndexOutOfBoundsException si fromIndex < 0 ou
toIndex > a.length
*/
static void sort(int[] a, int fromIndex, int toIndex)

Livre Java .book Page 669 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

669

La documentation indique que la mthode lance une exception si les valeurs dindice sont fausses.
Ce comportement fait partie du contrat que la mthode passe avec ses appelants. Si vous implmentez la mthode, vous devez respecter le contrat et lancer les exceptions indiques. Il ne conviendrait
pas dutiliser les assertions ici.
Faut-il dclarer que a nest pas null? Voil qui ne convient pas non plus. La documentation de la
mthode ne mentionne rien sur son comportement lorsque a vaut null. Les appelants ont le droit de
supposer que la mthode sera russie dans ce cas et quelle ne lancera pas derreur dassertion.
Supposons toutefois que le contrat de la mthode ait t lgrement diffrent :
@param a Le tableau trier (ne doit pas tre null)

Les appelants de la mthode ont t avertis quil est interdit dappeler la mthode avec un tableau
null. La mthode peut toutefois commencer avec lassertion
assert a!= null;

Les informaticiens dsignent ce type de contrat sous le terme de prcondition. La mthode initiale
ne disposait pas de prcondition sur ses paramtres, elle promettait un comportement bien dfini
dans tous les cas. La mthode rvise possde une seule prcondition : que a ne soit pas null. Si
lappelant ne respecte pas la prcondition, les paris ne tiennent plus et la mthode peut faire ce
quelle veut. En fait, une fois lassertion en place, la mthode aura un comportement assez imprvisible en cas dappel interdit. Parfois, elle lancera une erreur dassertion, parfois une exception de
pointeur nul, selon la configuration de son chargeur de classe.
INFO
Dans le JDK 5.0, la mthode Arrays.sort lance une exception NullPointerException si vous lappelez avec un
tableau null. Cest un bogue, prsent soit dans la spcification, soit dans limplmentation.

De nombreux programmeurs utilisent des commentaires pour documenter leurs hypothses. La


documentation du JDK en contient un bon exemple :
if (i % 3 == 0)
. . .
else if (i % 3 == 1)
. . .
else // (i % 3 == 2)
. . .

Lutilisation dune assertion prend tout son sens dans ce cas :


if (i % 3 == 0)
. . .
else if (i % 3 == 1)
. . .
else
{
assert i % 3 == 2;
. . .
}

Il serait bien entendu encore plus logique de rflchir ce problme de manire plus directe. Quelles
sont les valeurs possibles de i % 3 ? Si i est positif, le reste doit tre 0, 1 ou 2. Si i est ngatif, les

Livre Java .book Page 670 Jeudi, 25. novembre 2004 3:04 15

670

Au cur de Java 2 - Notions fondamentales

restes peuvent tre 1 ou 2. Par consquent, la vritable hypothse est que i nest pas ngatif.
Une meilleure assertion serait dcrire
assert i >= 0;

avant linstruction if.


Dans tous les cas, cet exemple fait bon usage des assertions comme contrle automatique pour le
programmeur. Comme vous pouvez le voir, les assertions constituent un outil tactique permettant de
tester et de dboguer un programme, tandis que la consignation est un outil stratgique conu pour
servir tout au long du cycle de vie dun programme.
java.lang.ClassLoader 1.0

void setDefaultAssertionStatus(boolean b) 1.4

Active ou dsactive les assertions pour toutes les classes charges par ce chargeur de classe qui
nont pas un statut explicite dassertion de classe ou de package.

void setClassAssertionStatus(String className, boolean b) 1.4

Active ou dsactive les assertions pour la classe donne ou ses classes internes.

void setPackageAssertionStatus(String packageName, boolean b) 1.4

Active ou dsactive les assertions pour toutes les classes du package donn et ses sous-packages.

void clearAssertionStatus() 1.4

Supprime tous les paramtres explicites sur le statut dassertion de classe et de package et dsactive
les assertions pour toutes les classes charges par ce chargeur de classe.

Les techniques de mise au point


Vous avez termin votre programme et lavez protg contre toute ventualit en capturant et en traitant effectivement toutes les exceptions. Vous lancez lexcution, et cela ne marche pas. Que faire ?
(Si vous navez jamais rencontr ce problme, passez sans remords au chapitre suivant.)
Il est prfrable de possder un systme de dbogage pratique et puissant. Ces outils de mise au
point font en gnral partie denvironnements professionnels de dveloppement comme Eclipse,
NetBeans ou JBuilder. Toutefois, avec une version trs rcente de Java qui ne sera pas supporte par
les environnements de dveloppement ou dans le cas dune plate-forme exotique, vous pouvez tre
oblig de dboguer avec la mthode traditionnelle, qui consiste insrer des instructions de trace
dans votre programme.

Quelques tours de main pour le dbogage


Voici quelques astuces pour un dbogage efficace, si vous vous trouvez sans aucun environnement
de mise au point :
1. On peut imprimer la valeur dune variable par
System.out.println("x="+x);

ou
Logger.global.info("x=" + x);

Livre Java .book Page 671 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

671

Si x est un nombre, il sera converti en une chane de caractres. Si cest un objet, Java appellera
sa mthode toString. Pour obtenir ltat de lobjet paramtre implicite, affichez ltat de cet
objet :
Logger.global.info("this=" + this);

La plupart des mthodes de la bibliothque de classes Java sefforcent de surcharger, sil y a lieu,
la mthode toString afin dinformer efficacement sur la classe. Cest parfait pour le dbogage.
Vous devez poursuivre le mme effort dans vos propres classes.
2. Une petite astuce apparemment peu connue consiste introduire une mthode main spare
lintrieur de chaque classe. Dans celle-ci, vous pouvez inclure un stub de test afin de tester la
classe de faon isole :
public class MyClass
{
mthodes et champs
. . .
public static void main(String[] args)
{
code de test
}
}

Crez quelques objets, appelez toutes les mthodes et vrifiez que chacune fonctionne. Vous
pouvez laisser toutes ces mthodes main en place et appeler la machine virtuelle Java de faon
spare sur chacun des fichiers afin dexcuter les tests. Lorsque vous lancez un applet, aucune
de ces mthodes main nest jamais appele. Lorsque vous lancez une application, la machine
virtuelle Java appelle uniquement la mthode main de la classe de dmarrage.
3. Si vous avez aim lastuce prcdente, consultez JUnit ladresse http://junit.org. JUnit est un
encadrement de test trs populaire qui facilite lorganisation des squences de test. Excutez les
tests ds que vous modifiez une classe et ajoutez un test cadre ds que vous retrouvez un bogue.
4. Un proxy de consignation est un objet dune sous-classe qui intercepte les appels de mthode, les
consigne et appelle la superclasse. Si, par exemple, vous rencontrez des problmes avec la
mthode setBackground dun panneau, vous pouvez crer un objet proxy comme instance
dune sous-classe anonyme :
JPanel panel = new
JPanel()
{
public void setBackground(Color c)
{
Logger.global.info("setBackground: c=" + c);
super.setBackground(c);
}
};

Ds que la mthode setBackground est appele, un message de journal est gnr. Pour savoir
qui a appel la mthode, gnrez une trace de pile (voir la prochaine astuce).
5. On peut sortir le contenu de la pile pour un objet exception grce la mthode printStackTrace de la classe Throwable. Le morceau de code suivant capture toutes les exceptions, affiche

Livre Java .book Page 672 Jeudi, 25. novembre 2004 3:04 15

672

Au cur de Java 2 - Notions fondamentales

lobjet exception et le contenu de la pile, puis propage lexception lintention de son gestionnaire potentiel :
try
{
. . .
}
catch (Throwable t)
{
t.printStackTrace();
throw t;
}

Vous navez mme pas besoin dintercepter une exception pour gnrer le traage dune pile.
Il vous suffit dinsrer linstruction
Thread.dumpStack();

nimporte quel endroit de votre code pour lobtenir.


6. En principe, le contenu de la pile saffiche par System.err. On peut lenvoyer dans un fichier
avec la mthode void printStackTrace(PrintWriter). Ou encore, pour afficher le contenu
de la pile dans une fentre, voici la faon de le placer dans une chane :
StringWriter out = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(out));
String trace = out.toString();

(Voir au Chapitre 12 les classes PrintWriter et StringWriter.)


7. Il est souvent pratique dintercepter les erreurs dun programme en les crivant dans un fichier.
Cependant, les erreurs sont envoyes System.err et non pas System.out. Par consquent,
on ne peut pas se contenter de les capturer en lanant linstruction
java MyProgram > errors.txt

Sous UNIX et Windows NT/2000/XP, cela se rsout facilement. Par exemple, si vous utilisez
bash comme shell, il vous suffit de capturer le flux derreurs par
java MyProgram 2> errors.txt

Pour capturer la fois et dans le mme fichier System.err et System.out, utilisez


java MyProgram 2>&1 errors.txt

Certains systmes dexploitation tels que Windows 95, 98 ou Me ne disposent pas dune
mthode aussi pratique. Voici le remde. Utilisez le programme Java suivant :
import java.io.*;
public class Errout
{
public static void main(String[] args) throws IOException
{
Process p = Runtime.getRuntime().exec(args);
BufferedReader err = new BufferedReader(new
InputStreamReader(p.getErrorStream()));
String line;
while ((line = err.readLine())!= null)
System.out.println(line);
}
}

Livre Java .book Page 673 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

673

Puis excutez votre programme :


java Errout java MyProgram > errors.txt

INFO C++
La faon la plus efficace pour obtenir le mme rsultat dans Windows consiste compiler ce programme C dans un
fichier errout.exe:
#include <io.h>
#include <stdio.h>
#include <process.h>
int main(int argc, char* argv[])
{
dup2(1, 2); /* faire aller stderr stdout */
execvp(argv[1], argv+1);
return 0;
}

Ensuite, excutez :
errout java MyProgram > errors.txt

8. Pour observer le chargement des classes, excutez la machine virtuelle Java avec le drapeau
-verbose. Vous obtenez une premire sortie imprimante telle que
[Opened
[Opened
[Opened
[Opened
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
...

/usr/local/jdk5.0/jre/lib/rt.jar]
/usr/local/jdk5.0/jre/lib/jsse.jar]
/usr/local/jdk5.0/jre/lib/jce.jar]
/usr/local/jdk5.0/jre/lib/charsets.jar]
java.lang.Object from shared objects file]
java.io.Serializable from shared objects file]
java.lang.Comparable from shared objects file]
java.lang.CharSequence from shared objects file]
java.lang.String from shared objects file]
java.lang.reflect.GenericDeclaration from shared objects file]
java.lang.reflect.Type from shared objects file]
java.lang.reflect.AnnotatedElement from shared objects file]
java.lang.Class from shared objects file]
java.lang.Cloneable from shared objects file]

Ceci peut tre utile, loccasion, pour diagnostiquer des problmes de chemin de classe.
9. Pour espionner le contenu dune fentre Swing, dont les composants sont si agrablement
aligns, il suffit de faire Ctrl+Maj+F1 pour obtenir laffichage de tous les composants de la
hirarchie :
FontDialog[frame0,0,0,300x200,layout=java.awt.BorderLayout,...
javax.swing.JRootPane[,4,23,292x173,layout=javax.swing.JRootPane$RootLayout,..
.
javax.swing.JPanel[null.glassPane,0,0,292x173,hidden,layout=java.awt.FlowLayou
t,...
javax.swing.JLayeredPane[null.layeredPane,0,0,292x173,...
javax.swing.JPanel[null.contentPane,0,0,292x173,layout=java.awt.GridBagLayout,
...
javax.swing.JList[,0,0,73x152,alignmentX=null,alignmentY=null,...
javax.swing.CellRendererPane[,0,0,0x0,hidden]
javax.swing.DefaultListCellRenderer$UIResource[,-73,-19,0x0,...

Livre Java .book Page 674 Jeudi, 25. novembre 2004 3:04 15

674

Au cur de Java 2 - Notions fondamentales

javax.swing.JCheckBox[,157,13,50x25,layout=javax.swing.OverlayLayout,...
javax.swing.JCheckBox[,156,65,52x25,layout=javax.swing.OverlayLayout,...
javax.swing.JLabel[,114,119,30x17,alignmentX=0.0,alignmentY=null,...
javax.swing.JTextField[,186,117,105x21,alignmentX=null,alignmentY=null,...
javax.swing.JTextField[,0,152,291x21,alignmentX=null,alignmentY=null,...

10. Si vous avez crit un composant Swing spcial et si ce composant ne saffiche pas correctement,
vous devriez apprcier le Swing graphics debugger. Mme si vous ncrivez pas de composant,
il est utile et mme amusant de visualiser la faon exacte dont le contenu dun composant est
dessin. Pour activer le dbogage dun composant Swing, utilisez la mthode setDebugGraphicsOptions de la classe JComponent, avec les options suivantes :
DebugGraphics.FLASH_OPTION

"Flashe" en rouge chaque ligne, rectangle ou texte avant


de le dessiner.

DebugGraphics.LOG_OPTION

Affiche un message chaque opration de dessin.

DebugGraphics.BUFFERED_OPTION

Affiche les oprations effectues dans le tampon hors


cran.

DebugGraphics.NONE_OPTION

Dsactive le dbogage graphique.

Il semble que, pour faire fonctionner correctement cette option, il soit ncessaire de dsactiver la
stratgie de "double buffrisation" utilise par Swing pour rduire le scintillement au moment de
la mise jour dune fentre. Voici la recette pour activer cette option de "flash" :
RepaintManager.currentManager(getRootPane())
.setDoubleBufferingEnabled(false);
((JComponent)getContentPane())
.setDebugGraphicsOptions(DebugGraphics.FLASH_OPTION);

Ajoutez ces lignes la fin de votre constructeur de cadre. A lexcution, vous verrez le contenu
du panneau apparatre lentement. Pour un dbogage plus prcis, on peut appeler setDebugGraphicsOptions pour un composant particulier. Les puristes peuvent ajuster la dure, le nombre et
la couleur des flashes consultez laide en ligne de la classe DebugGraphics pour plus de
dtails.
11. Le JDK 5.0 ajoute loption -Xlint au compilateur pour retrouver les problmes de code
communs. Par exemple, si vous compilez avec la commande
javac -Xlint:fallthrough

le compilateur signale labsence dinstructions break dans les instructions switch (le terme "lint"
dcrivait lorigine un outil permettant de retrouver des problmes potentiels dans les programmes C ; il sapplique maintenant de manire gnrique aux outils qui retrouvent les constructions
douteuses, sans tre interdites). Les options suivantes sont disponibles :
-Xlint ou -Xlint:all

Ralise toutes les vrifications.

-Xlint:deprecation

Identique -deprecation, recherche les mthodes


dprcies.

-Xlint:fallthrough

Recherche les instructions break manquantes dans les


instructions switch.

-Xlint:finally

Signale les clauses finally qui ne peuvent pas se terminer


normalement.

Livre Java .book Page 675 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

675

-Xlint:none

Ne ralise aucune vrification.

-Xlint:path

Vrifie lexistence de tous les rpertoires sur le chemin


de classe et celle du chemin source.

-Xlint:serial

Signale les classes srialisables ne contenant pas


serialVersionUID (voir Chapitre 12).

-Xlint:unchecked

Signale les conversions peu sres entre des types gnriques et des types bruts (voir Chapitre 13).

12. Le JDK 5.0 prend maintenant en charge la surveillance et la gestion des applications Java,
ce qui permet dinstaller des agents dans la machine virtuelle pour suivre la consommation
de mmoire, lutilisation des threads, le chargement de classes, etc. Cette fonctionnalit est
particulirement importante pour de gros programmes Java, au fonctionnement prolong,
par exemple les serveurs dapplications. En guise de dmonstration, le JDK est livr avec un
outil graphique appel jconsole qui propose des statistiques sur les performances dune
machine virtuelle (voir Figure 11.4). Pour activer la surveillance, lancez la machine
virtuelle avec loption -Dcom.sun.management.jmxremote. Retrouvez ensuite lID du
process du systme dexploitation qui excute la machine virtuelle. Sous UNIX/Linux,
excutez lutilitaire ps; sous Windows, utilisez le Gestionnaire de tches. Lancez ensuite le
programme jconsole:
java -Dcom.sun.management.jmxremote MyProgram.java
jconsole IDprocess

Figure 11.4
Le programme
jconsole.

Livre Java .book Page 676 Jeudi, 25. novembre 2004 3:04 15

676

Au cur de Java 2 - Notions fondamentales

13. Si vous lancez la machine virtuelle avec le drapeau -Xprof, il excute un profiler rudimentaire
qui suit les mthodes de votre code ayant t le plus souvent excutes. Les informations de
profilage sont envoyes System.out. La sortie vous indique aussi les mthodes compiles par
le compilateur en juste temps (just-in-time).
ATTENTION
Les options -X du compilateur ne sont pas officiellement prises en charge et risquent de ne pas apparatre dans toutes
les versions du JDK. Excutez java -X pour obtenir une liste de toutes les options non standard.

Utiliser une fentre de console


Si vous lancez un applet dans un navigateur, il se peut quon ne puisse pas voir les messages envoys
System.out. La plupart des navigateurs disposent de fentres de console Java (vrifiez dans le
systme daide de votre navigateur). Par exemple, Netscape Navigator en a une, tout comme Internet
Explorer 4 et suivants. Si vous utilisez le plug-in Java, vrifiez la bote daffichage de la console Java
dans le panneau de configuration (voir Chapitre 10).
En outre, la fentre de console Java dispose dun ensemble de barres de dfilement de faon vous
permettre de rcuprer les messages qui ont dfil en dehors de la fentre. Les utilisateurs de la fentre de la console Java bnficient ainsi dun rel avantage par rapport la fentre de shell DOS dans
laquelle les sorties System.out saffichent normalement.
Nous vous fournissons une classe de fentre identique, afin que vous puissiez profiter du mme
avantage, en visualisant vos messages de dbogage lintrieur dune fentre lorsque vous mettez au
point un programme. La Figure 11.5 prsente la classe ConsoleWindow en action.
Cette classe est dun usage facile. Il suffit dappeler
ConsoleWindow.init()

Ensuite, imprimez normalement vers System.out ou System.err.


Figure 11.5
La fentre de console.

Livre Java .book Page 677 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

677

Lexemple 11.5 liste le code de la classe ConsoleWindow. Comme vous pouvez le voir, la classe est
trs simple. Les messages sont affichs dans une JTextArea lintrieur dun panneau JScrollPane. Nous appelons les mthodes System.setOut et System.setErr pour diriger les sorties et flux
derreurs vers un flux spcial qui ajoute chaque message dans la zone de texte.
Exemple 11.5 : ConsoleWindow.java
import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
java.io.*;

/**
Une fentre qui affiche les mots (bytes) envoys
et System.err

System.out

*/
public class ConsoleWindow
{
public static void init()
{
JFrame frame = new JFrame();
frame.setTitle("ConsoleWindow");
final JTextArea output = new JTextArea();
output.setEditable(false);
frame.add(new JScrollPane(output));
frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
frame.setLocation(DEFAULT_LEFT, DEFAULT_TOP);
frame.setFocusableWindowState(false);
frame.setVisible(true);
// dfinir un PrintStream qui envoie ses mots la
// zone de texte de sortie
PrintStream consoleStream = new PrintStream(new
OutputStream()
{
public void write(int b) {} // jamais appel
public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
// dfinir System.out et System.err sur ce flux
System.setOut(consoleStream);
System.setErr(consoleStream);
}
public
public
public
public
}

static
static
static
static

final
final
final
final

int
int
int
int

DEFAULT_WIDTH = 300;
DEFAULT_HEIGHT = 200;
DEFAULT_LEFT = 200;
DEFAULT_TOP = 200;

Livre Java .book Page 678 Jeudi, 25. novembre 2004 3:04 15

678

Au cur de Java 2 - Notions fondamentales

Tracer les vnements AWT


Si vous devez crire une interface utilisateur labore en Java, il vous faut savoir quels vnements
envoie AWT quels composants. Malheureusement, la documentation AWT est assez indigente sur
ce point. Par exemple, nous voulons faire apparatre des phrases daide dans la ligne dtat lorsque
lutilisateur dplace la souris sur les diverses parties de lcran. AWT gnre des vnements de
souris et de focus quil vous faut intercepter.
Nous vous fournissons une classe EventTrace trs utile pour espionner ces vnements. Elle affiche
toutes les mthodes de gestion des vnements avec leurs paramtres. La Figure 11.6 affiche les
vnements tracs.
Pour espionner les messages, il suffit dajouter les composants dont les vnements doivent tre
suivis, lintrieur dun traceur dvnement :
EventTracer tracer = new EventTracer();
tracer.add(frame);

Cela affiche un texte dcrivant tous les vnements, comme ceci :


public abstract void java.awt.event.MouseListener.mouseExited(
java.awt.event.MouseEvent):
java.awt.event.MouseEvent[MOUSE_EXITED,(408,14),button=0,clickCount=0]
on javax.swing.JButton[,0,345,400x25,...]
public abstract void java.awt.event.FocusListener.focusLost(
java.awt.event.FocusEvent):
java.awt.event.FocusEvent[FOCUS_LOST,temporary,opposite=null] on
javax.swing.JButton[,0,345,400x25,...]

Peut-tre dsirez-vous capturer ces sorties dans un fichier ou une fentre de console, comme nous
lavons vu dans les sections prcdentes ?
LExemple 11.6 correspond la classe EventTracer. Lide qui sy cache est simple, mme si son
implmentation semble mystrieuse.
1. Lorsque vous ajoutez un composant au traceur dvnement dans la mthode add, la classe
introspection des Java Beans analyse le composant la recherche des mthodes de la forme
void addXxxListener(XxxEvent) (voir le Chapitre 8 du Volume 2). Pour chaque mthode
correspondante un EventSetDescriptor est gnr. Nous passons chaque descripteur la
mthode addListener.
2. Si le composant est un conteneur, nous numrons ses composants et pouvons appeler chacun
dentre eux de faon rcursive.
3. addListener est appele avec deux paramtres : le composant dont nous voulons espionner les
vnements et le descripteur dvnement. La mthode getListenerType de la classe EventSetDescriptor renvoie un objet Class qui dcrit linterface dobservation dvnement telle
que ActionListener ou ChangeListener. Nous crons un objet proxy pour cette interface. Le
gestionnaire de proxy se contente dimprimer le nom et le paramtre dvnement de la mthode
event invoque. La mthode getAddListenerMethod de la classe EventSetDescriptor
renvoie un objet Method que nous utilisons pour ajouter au composant lobjet proxy en tant
quobservateur dvnement.
Ce programme est un bon exemple de la puissance du mcanisme de rflexion. Nous navons pas
besoin de coder le fait que la classe JButton possde une mthode addAdjustmentListener,

Livre Java .book Page 679 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

679

puisque quun JSlider possde une mthode addChangeListener. Le mcanisme de rflexion


dcouvre ces faits pour nous.
Figure 11.6
Excution de la classe
EventTracer.

INFO
Le mcanisme du proxy rend ce programme beaucoup plus simple. Dans les prcdentes ditions de ce livre, il nous
a fallu dfinir un mcanisme de veille (listener) qui implmente simultanment les interfaces MouseListener,

ComponentListener, FocusListener, KeyListener, ContainerListener, WindowListener, TextListener, AdjustmentListener et ItemListener, ainsi quune vingtaine dautres mthodes qui impriment
le paramtre dvnement. Le mcanisme du proxy est expliqu la fin du Chapitre 6.

LExemple 11.7 teste le traceur dvnement. Le programme affiche un cadre avec un bouton et une
tirette et trace les vnements gnrs par ces composants.
Exemple 11.6 : EventTracer.java
import
import
import
import

java.awt.*;
java.awt.event.*;
java.beans.*;
java.lang.reflect.*;

public class EventTracer


{
public EventTracer()
{
// le gestionnaire pour tous les proxies dvnement
handler = new
InvocationHandler()
{
public Object invoke(Object proxy,
Method method, Object[] args)
{
System.out.println(method+":"+args[0]);

Livre Java .book Page 680 Jeudi, 25. novembre 2004 3:04 15

680

Au cur de Java 2 - Notions fondamentales

return null;
}
};
}
/**
Ajoute des traceurs dvnements pour tous les vnements
que ce composant et ses enfants peuvent couter
@param c Un composant
*/
public void add(Component c)
{
try
{
// rcuprer tous les vnements que ce composant peut couter
BeanInfo info = Introspector.getBeanInfo(c.getClass());
EventSetDescriptor[] eventSets
= info.getEventSetDescriptors();
for (EventSetDescriptor eventSet: eventSets)
addListener(c, eventSet);
}
catch (IntrospectionException e) {}
// ok pour ne pas ajouter dcouteurs si lexception est lance
if (c instanceof Container)
{
// rcuprer tous les enfants et appeler add
// de manire rcurrente
for (Component comp: ((Container) c).getComponents())
add(comp);
}
}
/**
Ajouter un couteur au jeu dvnements donn
@param c Un composant
@param eventSet Un descripteur dune interface dcouteur
*/
public void addListener(Component c, EventSetDescriptor eventSet)
{
// crer lobjet proxy pour ce type dcouteur
// et acheminer tous les appels au gestionnaire
Object proxy = Proxy.newProxyInstance(null,
new Class[] { eventSet.getListenerType() }, handler);
// ajouter le proxy au composant sous forme dcouteur
Method addListenerMethod = eventSet.getAddListenerMethod();
try
{
addListenerMethod.invoke(c, proxy);
}
catch(InvocationTargetException e) {}
catch(IllegalAccessException e) {}
// ok pour ne pas ajouter dcouteur si lexception est lance
}
private InvocationHandler handler;
}

Livre Java .book Page 681 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

681

Exemple 11.7 : EventTracerTest.java


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class EventTracerTest
{
public static void main(String[] args)
{
JFrame frame = new EventTracerFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class EventTracerFrame extends JFrame
{
public EventTracerFrame()
{
setTitle("EventTracerTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter un curseur et un bouton
add(new JSlider(), BorderLayout.NORTH);
add(new JButton("Test"), BorderLayout.SOUTH);
// intercepter tous les vnements des composants dans le cadre
EventTracer tracer = new EventTracer();
tracer.add(this);
}
public static final int DEFAULT_WIDTH = 400;
public static final int DEFAULT_HEIGHT = 400;
}

Le robot awt
La version 1.3 de Java 2 ajoute une classe Robot que vous pouvez utiliser pour envoyer des frappes
de clavier et des clics de souris vers nimporte quel programme AWT. Cette classe a pour vocation
de permettre lautomatisation du test des interfaces utilisateur.
Pour obtenir un robot, il vous faut dabord obtenir un objet GraphicsDevice. On obtient lcran par
dfaut par une suite dappels :
GraphicsEnvironment environment
= GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice screen
= environment.getDefaultScreenDevice();

Puis vous construisez un robot :


Robot robot = new Robot(screen);

Pour envoyer une frappe de clavier, demandez au robot de simuler une frappe de touche suivie dune
sortie de touche :
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);

Livre Java .book Page 682 Jeudi, 25. novembre 2004 3:04 15

682

Au cur de Java 2 - Notions fondamentales

Pour un clic de souris, il vous faut dplacer la souris, puis appuyer et relcher le bouton :
robot.mouseMove(x, y);
// x et v sont les coordonnes absolues des pixels lcran
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);

Lide consiste simuler le clavier et la souris, puis de prendre un clich de lcran pour voir si
lapplication sest comporte comme prvu. Vous capturez lcran avec la mthode createScreenCapture:
Rectangle rect = new Rectangle(x, y, width, height);
BufferedImage image = robot.createScreenCapture(rect);

Les coordonnes du rectangle se rfrent galement des pixels en absolu.


Pour finir, vous souhaitez en gnral ajouter un petit dlai entre les instructions du robot de telle
faon que lapplication puisse tre en rythme. Utilisez la mthode delay et donnez-lui un temps
dattente en millisecondes. Par exemple :
robot.delay(1000); // delai de 1000 millisecondes

Le programme de lExemple 11.8 vous montre comment utiliser le robot. Robot teste le programme
de test de boutons vu au Chapitre 8. Pour commencer, appuyez sur la barre despace. Cela active le
bouton gauche. Puis le robot attend deux secondes afin que vous puissiez voir ce qui se passe. Aprs
ce dlai, le robot simule la touche de tabulation et une autre frappe sur la barre despace pour cliquer
sur le bouton suivant. Pour finir, on simule un clic du troisime bouton de la souris (peut-tre faudrat-il ajuster les coordonnes X et Y du programme afin dappuyer rellement sur ce bouton). Le
programme se termine en ralisant une capture dcran et en laffichant dans un autre cadre (voir
Figure 11.7).
Figure 11.7
Capture dcran
du robot AWT.

Comme on le voit dans lexemple, la classe Robot nest pas en elle-mme adapte au test de linterface utilisateur. Au lieu de cela, elle constitue un bloc de base qui peut devenir lune des pierres de
touche dun outil de test. Un outil de test professionnel peut capturer, stocker et rafficher des scnarios

Livre Java .book Page 683 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

683

dinteraction dutilisateurs, et trouver les emplacements sur lcran de tous les composants. De cette
faon, les clics de la souris ne tombent pas au hasard. Au moment de lcriture de ce livre, le robot
vient juste de sortir et nous navons connaissance daucun outil complet de test destin aux interfaces
utilisateur. Nous esprons voir ces outils apparatre prochainement.
Exemple 11.8 : RobotTest.java
import
import
import
import

java.awt.*;
java.awt.event.*;
java.awt.image.*;
javax.swing.*;

public class RobotTest


{
public static void main(String[] args)
{
// crer un cadre avec un panneau de bouton
ButtonFrame frame = new ButtonFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
// joindre un robot au priphrique cran
GraphicsEnvironment environment
= GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice screen
= environment.getDefaultScreenDevice();
try
{
Robot robot = new Robot(screen);
run(robot);
}
catch (AWTException e)
{
e.printStackTrace();
}
}
/**
Excute un exemple de procdure de test
@param robot Le robot attach au priphrique cran
*/
public static void run(Robot robot)
{
// simuler une pression sur la barre espace
robot.keyPress( );
robot.keyRelease( );
// simuler une touche de tabulation suivie dune espace
robot.delay(2000);
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
robot.keyPress( );
robot.keyRelease( );
// simuler un clic de souris sur le bouton droit

Livre Java .book Page 684 Jeudi, 25. novembre 2004 3:04 15

684

Au cur de Java 2 - Notions fondamentales

robot.delay(2000);
robot.mouseMove(200, 50);
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
// capturer lcran et afficher limage
robot.delay(2000);
BufferedImage image = robot.createScreenCapture(
new Rectangle(0, 0, 400, 300));
ImageFrame frame = new ImageFrame(image);
frame.setVisible(true);
}
}
/**
Un cadre pour afficher une capture dcran
*/
class ImageFrame extends JFrame
{
/**
@param image Limage afficher
*/
public ImageFrame(Image image)
{
setTitle("Capture");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
JLabel label = new JLabel(new ImageIcon(image));
add(label);
}
public static final int DEFAULT_WIDTH = 450;
public static final int DEFAULT_HEIGHT = 350;
}
java.awt.GraphicsEnvironment 1.2
m

static GraphicsEnvironment getLocalGraphicsEnvironment()


Renvoie lenvironnement graphique local.

GraphicsDevice getDefaultScreenDevice()
Renvoie lcran par dfaut. Notez que les ordinateurs avec plusieurs moniteurs nont quun cran
virtuel par moniteur. Utilisez la mthode getScreenDevices pour obtenir la liste de tous les
crans.

java.awt.Robot 1.3
m

Robot(GraphicsDevice device)
Construit un robot dinteraction avec priphrique.

void keyPress(int key)

Livre Java .book Page 685 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

685

void keyRelease(int key)


Simule lenfoncement ou le relchement dune touche.
Paramtres :
key
Le code de touche. Voir la classe KeyStroke pour plus
dinformations sur les codes de touche.

void mouseMove(int x, int y)


Simule un mouvement de la souris.
Paramtres :
x,yLa position de la souris en coordonnes absolues et en pixels.

void mousePress(int eventMask)

void mouseRelease(int eventMask)


Simule une pression sur un bouton de la souris ou son relchement.
Paramtres :
eventMaskLe masque dvnement dcrivant les boutons de la souris. Voir la classe InputEvent
pour plus dinformations sur les masques dvnement.

void delay(int milliseconds)


Met le robot en attente pour le nombre de millisecondes donn en paramtre.

bufferedImage createScreenCapture(Rectangle rect)


Capture une partie de lcran.
Paramtres :
rectLe rectangle qui doit tre captur en coordonnes absolues et en pixels.

Utiliser un dbogueur
Dboguer en utilisant des instructions print est lune des plus agrables expriences qui soient. Vous
vous trouvez constamment ajouter et enlever des instructions et recompiler le programme.
Lusage dun dbogueur est plus efficace parce que celui-ci fait tourner votre programme jusqu ce
quil atteigne un point darrt prdtermin. A ce moment-l, vous pouvez observer tout ce qui vous
intresse.

Le dbogueur JDB
Le JDK inclut le JDB, qui est une ligne de commande de dbogage extrmement rudimentaire. Son
interface utilisateur est si minimaliste que vous ne souhaiteriez lutiliser quen dernier ressort. Il
sagit davantage dune dmonstration du concept que dun outil. Nanmoins, nous allons y consacrer une brve introduction parce quil existe des situations dans lesquelles il vaut mieux un dbogueur rudimentaire que pas de dbogueur du tout. Bien sr, de nombreux environnements de
programmation Java disposent de dbogueurs bien plus pratiques. Les principes de tous les dbogueurs sont les mmes. Vous voudrez peut-tre utiliser les exemples de cette section afin de tester
celui que vous prfrez.

Livre Java .book Page 686 Jeudi, 25. novembre 2004 3:04 15

686

Au cur de Java 2 - Notions fondamentales

Les Exemples 11.9 11.11 utilisent une version dlibrment altre du programme ButtonTest
du Chapitre 8. Il nous a fallu dmonter le programme et placer chaque classe dans un fichier spar
afin de passer outre une limitation de certains dbogueurs.
Lorsque vous cliquez sur lun quelconque des boutons, rien ne se produit. Regardez le code source.
Les boutons sont censs dfinir la couleur de fond en fonction du nom du bouton.
Exemple 11.9 : BuggyButtonTest.java
import javax.swing.*;
public class BuggyButtonTest
{
public static void main(String[] args)
{
BuggyButtonFrame frame = new BuggyButtonFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}

Exemple 11.10 : BuggyButtonFrame.java


import java.awt.*;
import javax.swing.*;
public class BuggyButtonFrame extends JFrame
{
public BuggyButtonFrame()
{
setTitle("BuggyButtonTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// ajouter un panneau au cadre
BuggyButtonPanel panel = new BuggyButtonPanel();
add(panel);
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}

Exemple 11.11 : BuggyButtonPanel.java


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class BuggyButtonPanel extends JPanel
{
public BuggyButtonPanel()
{
ActionListener listener = new ButtonListener();
JButton yellowButton = new JButton("Yellow");
add(yellowButton);
yellowButton.addActionListener(listener);

Livre Java .book Page 687 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

687

JButton blueButton = new JButton("Blue");


add(blueButton);
blueButton.addActionListener(listener);
JButton redButton = new JButton("Red");
add(redButton);
redButton.addActionListener(listener);
}
private class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String arg = event.getActionCommand();
if (arg.equals("yellow"))
setBackground(Color.yellow);
else if (arg.equals("blue");
setBackground(Color.blue);
else if (arg.equals("red"))
setBackground(Color.red);
}
}
}

Dans un programme aussi court, vous pouvez trouver le bogue simplement en lisant le code source.
Mais nous allons imaginer quil sagit dun programme trs complexe. Nous dcrivons ci-aprs
comment lancer le dbogueur pour trouver lerreur.
Pour utiliser le JDB, vous commencez par compiler votre programme avec loption -g, comme dans
cet exemple :
javac -g BuggyButtonTest.java BuggyButtonFrame.java
BuggyButtonPanel.java

Avec cette option, le compilateur ajoutera les noms des variables locales ainsi que dautres informations
pour le dbogage dans les fichiers de classes. Lancez le dbogueur :
jdb BuggyButtonTest

Il saffiche approximativement comme ceci :


Initializing jdb...
>

Linvite > signifie que le dbogueur attend une commande. Le Tableau 11.4 rpertorie la liste des
commandes. Les lments placs entre crochets [...] sont facultatifs ; la marque dun (s) donne la
possibilit davoir plusieurs arguments spars par des espaces.
Tableau 11.4 : Les commandes du dbogueur

threads[groupedethreads]

Donne la liste des threads

thread thread id

Fixe le thread par dfaut

suspend [thread_id(s)]

Suspend le ou les threads (par dfaut : all)

resume [thread_id(s)]

Relance le ou les threads (par dfaut : all)

Livre Java .book Page 688 Jeudi, 25. novembre 2004 3:04 15

688

Au cur de Java 2 - Notions fondamentales

Tableau 11.4 : Les commandes du dbogueur (suite)

where [thread_id] ou all

Vidage de la pile dun thread

wherei [thread_id] ou all

Vidage de la pile dun thread et de linformation de compteur du


programme

threadgroups

Donne la liste des groupes de threads

threadgroup nom

Fixe le groupe de thread courant

print nom(s)

Affiche lobjet ou le champ

dump nom(s)

Affiche toute linformation concernant un objet

locals

Affiche toutes les variables locales courantes

classes

Donne la liste des classes connues

methods classe

Donne les mthodes dune classe

stop in classe.mthode

Place un point darrt dans une mthode

stop at classe:ligne

Place un point darrt dans une ligne du programme

up [n]

Dplace le pointeur de pile dun thread vers le sommet

down [n]

Dplace le pointeur de pile dun thread vers le bas

clear classe:ligne

Supprime un point darrt

step

Excute partir de la ligne courante en suivant les appels en pas pas

stepi

Excute linstruction courante

step up

Excute jusqu la fin de la mthode courante

next

Excute partir de la ligne courante en sautant le code appel

cont

Reprend lexcution aprs un point darrt

catch classe

Intercepte une exception

ignore classe

Ignore une exception

list [ligne]

Affiche le code source

use [chemin]

Affiche et/ou modifie le chemin du code source

memory

Donne loccupation mmoire

gc

Libre les objets inutiliss

load classe

Charge une classe Java mettre au point

Livre Java .book Page 689 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

689

Tableau 11.4 : Les commandes du dbogueur (suite)

run [classe [args]]

Lance lexcution dune classe Java charge

!!

Rpte la commande prcdente

help (ou ?)

Donne la liste des commandes

exit (ou quit)

Termine la session de dbogage

Nous ne prsentons dans ce cas lutilisation que des commandes les plus utiles du JDB. Lide de
base est simple : placer un ou plusieurs points darrt, puis lancer le programme. Le programme
sarrte sur le premier point darrt atteint, permettant dexaminer les valeurs des variables locales
pour en valider le comportement.
Pour installer un point darrt, utilisez la syntaxe
stop in classe.mthode

ou
stop at classe:ligne

Pour placer un point darrt dans la mthode actionPerformed de BuggyButtonTest:


stop in BuggyButtonPanel$ButtonListener.actionPerformed

Pour lancer le programme, faire


run

Le programme sexcute, mais le point darrt ne sera pas atteint avant lentre dans la mthode
actionPerformed. Il faut donc cliquer sur le bouton Yellow. Le dbogueur sarrte sur le start de
la mthode actionPerformed. Les indications suivantes saffichent :
Breakpoint hit: thread="AWT-EventQueue-0",
BuggyButtonPanel$ButtonListener.actionPerformed(),
line=28, bci=0
28 String arg = event.getActionCommand();

La commande list donne la ligne courante et quatre lignes au-dessus et au-dessous. Les lignes sont
numrotes. Exemple :
24
25
26
27
28=>
29
30
31
32
33

private class ButtonListener implements ActionListener


{
public void actionPerformed(ActionEvent event)
{
String arg = event.getActionCommand();
if (arg.equals("yellow"))
setBackground(Color.yellow);
else if (arg.equals("blue"))
setBackground(Color.blue);
else if (arg.equals("red"))

La commande locals fournit toutes les variables locales, par exemple :


Method arguments:
event = instance of java.awt.event.ActionEvent(id=698)
Local variables:

Livre Java .book Page 690 Jeudi, 25. novembre 2004 3:04 15

690

Au cur de Java 2 - Notions fondamentales

Pour plus de dtails, se servir de


dump variable

La commande
dump event

affiche tous les champs dune instance de la variable evt:


event = instance of java.awt.event.ActionEvent(id=698) {
SHIFT_MASK: 1
CTRL_MASK: 2
META_MASK: 4
ALT_MASK: 8
ACTION_FIRST: 1001
ACTION_LAST: 1001
ACTION_PERFORMED: 1001
actionCommand: "Yellow"
modifiers: 0
serialVersionUID: -7671078796273832149
. . .

Il y a deux commandes de base pour faire du pas pas dans un programme. La commande step
avance en pas pas en suivant compltement les appels de mthode. Il est plus sr dutiliser la
commande next qui passe la ligne suivante du code appelant sans entrer dans le dtail
dautres appels de mthode. Tapez next deux fois de suite, puis list pour voir o vous tes
arriv.
Le programme sarrte la ligne 31 :
27
28
29
30
31 =>
32
33
34
35

{
String arg = event.getActionCommand();
if (arg.equals("yellow"))
setBackground(Color.yellow);
else if (arg.equals("blue"))
setBackground(Color.blue);
else if (arg.equals("red"))
setBackground(Color.red);
}

Ce nest pas ce qui tait prvu. Normalement, le programme doit appeler setColor(Color.yellow),
puis quitter la mthode.
Ralisez un Dump de la variable arg:
arg = "Yellow"

Nous comprenons lerreur. Largument arg est en ralit "Yellow" avec un Y majuscule, mais le test
est crit ainsi :
if (arg.equals("yellow"))

avec un j minuscule. Mystre clairci !


Pour terminer la session de mise au point, tapez
quit

Nous voyons travers cet exemple que ce dbogueur peut tre utilis pour trouver une erreur, mais
linterface de ligne de commande est trs pratique. Pensez utiliser list et locals chaque fois que

Livre Java .book Page 691 Jeudi, 25. novembre 2004 3:04 15

Chapitre 11

Exceptions et mise au point

691

vous ne savez plus o vous tes. Mais si vous avez le choix, un meilleur dbogueur est indispensable
pour un vritable travail de mise au point.

Le dbogueur Eclipse
Eclipse dispose dun dbogueur puissant et pratique dot des principales caractristiques requises.
En particulier, vous pouvez visualiser les points darrt, inspecter les variables et raliser du pas
pas dans un programme.
Pour visualiser un point darrt, dplacez le curseur vers la ligne dsire et slectionnez Run/Toggle
Line Breakpoint depuis le menu. La ligne du point darrt saffiche en surbrillance (voir
Figure 11.8).
Figure 11.8
Un point darrt dans
le dbogueur Eclipse.

Pour lancer le dbogage, choisissez Run/Debug As/Java Application dans le menu. Le programme se
lance. Dfinissez un point darrt dans la premire ligne de la mthode actionPerformed.
Lorsque le dbogueur sarrte un point darrt, vous voyez la pile dappel et les variables locales
(voir Figure 11.9).
Pour avancer dun pas dans lapplication, choisissez Run/Step into (F5) ou Run/Step over (F6). Dans
notre exemple, appuyez deux fois sur F6 pour voir comment le programme ignore la commande
setBackground(Color.yellow). Regardez ensuite la valeur de arg pour en connatre la raison
(voir Figure 11.10).

Livre Java .book Page 692 Jeudi, 25. novembre 2004 3:04 15

692

Au cur de Java 2 - Notions fondamentales

Figure 11.9
Arrt
un point darrt.

Figure 11.10
Etude des variables.

Comme vous pouvez le voir, le dbogueur Eclipse est beaucoup plus facile utiliser que le JDB
parce que vous disposez dune information visuelle vous indiquant o vous vous trouvez dans le
programme. Etablir des points darrt et inspecter les variables est galement plus facile. Cest typique
des dbogueurs intgrs dans un environnement de dveloppement.
Ce chapitre a introduit la notion de gestion des exceptions en vous donnant quelques astuces de test
et de dbogage. Le prochain chapitre traite des flux.

Livre Java .book Page 693 Jeudi, 25. novembre 2004 3:04 15

12
Les flux et les fichiers
Au sommaire de ce chapitre

Les flux
La faune des flux
Les flux de fichiers ZIP
Lutilisation des flux
Les flux dobjets
La gestion des fichiers
Nouvelles E/S
Expressions ordinaires
Nous verrons, dans ce chapitre, comment travailler avec des fichiers et des rpertoires, en particulier
les mthodes de lecture et dcriture de linformation pour les fichiers. Nous expliquerons aussi le
mcanisme de srialisation des objets, qui permet de sauvegarder des objets aussi facilement que des
donnes textuelles ou numriques. Nous aborderons ensuite les diverses amliorations apportes
dans le package "new I/O" java.nio, prsent dans le JDK 1.4. Nous clturerons ce chapitre par
une discussion sur les expressions ordinaires, mme si elles ne sont pas vritablement lies aux flux
et aux fichiers. Nous navons pourtant pas trouv de meilleur endroit pour traiter de ce sujet, tout
comme lquipe Java, semble-t-il ! La spcification API dexpression ordinaire a t attache la
requte de spcification pour la fonctionnalit "new I/O" du JDK 1.4.

Les flux
Les techniques dentre/sortie ne sont pas un sujet trs sduisant, mais vos applications et, dans
certains cas, vos programmes vont se trouver trs limits. Ce chapitre explique comment prendre de
linformation en entre partir de nimporte quelle source de donnes capable dmettre une suite
doctets symtriquement et comment envoyer en sortie de linformation vers toute destination acceptant une suite doctets. Ces sources et destinations des squences doctets peuvent tre des fichiers,
et cest souvent le cas, mais galement des connexions sur un rseau et mme des blocs en mmoire.

Livre Java .book Page 694 Jeudi, 25. novembre 2004 3:04 15

694

Au cur de Java 2 - Notions fondamentales

Il faut garder lesprit ce caractre gnral des entres/sorties : par exemple, linformation stocke
dans des fichiers est traite pratiquement de la mme faon que celle provenant dune connexion
rseau (pour la programmation rseau, se reporter au second tome de cet ouvrage). Bien entendu,
mme si le stockage des donnes se rduit toujours en dfinitive une suite doctets, il est souvent
plus efficace de considrer que les donnes possdent une structuration de plus haut niveau, comme
une suite de caractres ou dobjets. Pour cette raison, nous ninsistons pas sur les entres/sorties de
bas niveau et nous nous concentrons sur les possibilits de niveau suprieur.
Dans le langage de programmation Java, lobjet partir duquel on peut lire une suite doctets se
nomme flux dentre. Par ailleurs, on appelle flux de sortie lobjet vers lequel on peut crire une suite
doctets. Ce sont les classes abstraites InputStream et OutputStream qui implmentent ces deux
types de flux. De plus, comme les flux doctets conviennent mal au traitement dinformations codes
en Unicode (on se souvient quUnicode utilise deux octets par unit de code), il a t introduit une
hirarchie de classes spciale pour le traitement des caractres Unicode : ces classes hritent des
classes abstraites Reader et Writer, qui possdent des oprations dcriture et de lecture reconnaissant des units de code Unicode de deux octets et non des caractres dun seul octet.
Nous avons parl des classes abstraites au Chapitre 5. Une classe abstraite apporte essentiellement
un mcanisme de factorisation du comportement commun dun ensemble de classes un niveau
suprieur. Cela conduit un code plus propre et une meilleure lisibilit de larbre dhritage. Cette
approche est utilise pour les entres/sorties en Java.
Java fait driver, partir de quatre classes abstraites, une multitude de classes concrtes que vous
serez amen frquenter, pour la plupart.

Lire et crire des octets


La classe InputStream possde une mthode abstraite :
abstract int read()

Cette mthode lit un seul octet et renvoie cet octet ou bien 1 si la fin de la source de donnes a t
atteinte. Le concepteur dune classe concrte pour un flux dentre va surcharger cette mthode afin
de la doter des fonctionnalits ncessaires. Ainsi pour la classe FileInputStream, cette mthode lit
un seul octet dans un fichier. Lobjet prdfini System.in de la sous-classe de InputStream permet
de saisir linformation partir du clavier.
La classe InputStream possde aussi des mthodes non abstraites pour lire un tableau doctets ou
pour liminer un certain nombre doctets. Ces mthodes font appel la mthode abstraite read.
Ainsi, les sous-classes nauront surcharger quune seule mthode.
De la mme manire, la classe OutputStream dfinit la mthode abstraite :
abstract void write(int b)

pour crire un octet vers une destination.


Les mthodes read et write peuvent toutes deux bloquer un thread jusqu ce que le flux soit effectivement lu ou crit. Cela signifie que, si loctet ne peut tre immdiatement lu ou crit (le plus
souvent dans le cas dune connexion rseau charge), Java suspend le thread effectuant cet appel.
Cela permet aux autres threads deffectuer du travail utile pendant que la mthode attend que le flux
redevienne accessible.

Livre Java .book Page 695 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

695

La mthode available dtermine le nombre doctets accessibles en lecture un moment donn.


On voit que ce morceau de code ne risque pas de rester en attente :
int bytesAvailable = in.available();
if (bytesAvailable > 0)
{
byte[] data = new byte[bytesAvailable];
in.read(data);
}

A lissue dune opration dcriture ou de lecture dans un flux, il faut le fermer en appelant la
mthode close. Cet appel libre les ressources du systme dexploitation dont le nombre est limit.
Si une application ouvre de nombreux flux sans prendre soin de les fermer, elle peut puiser les
ressources du systme. La fermeture dun flux de sortie a aussi pour effet de vider le tampon utilis
par le flux de sortie : tous les caractres se trouvant en transit dans un tampon afin de pouvoir tre
regroups en paquets sont mis. Aussi, si vous ne fermez pas un fichier, le dernier paquet doctets
risque de ne jamais partir. Il est possible de forcer un vidage par la mthode flush.
Les programmeurs Java auront peu lutilit dune classe flux qui ne possde que des mthodes
concrtes encapsulant les fonctions de base read et write, car un programme na que rarement
besoin de lire ou dcrire des flux doctets. Les donnes que vous allez rencontrer contiennent en
gnral des nombres, des chanes et des objets.
Java fait driver de nombreuses classes flux des classes de base InputStream et OutputStream qui
vont prcisment permettre de traiter les donnes dans ces formats habituels et non au plus bas
niveau de loctet.
java.io.InputStream 1.0

abstract int read()

Lit puis renvoie un octet de donnes. La mthode read renvoie 1 la fin du flux.

int read(byte[] b)

Lit dans un tableau doctets et renvoie le nombre rel doctets lus ou 1 la fin du flux. La
mthode read lit au plus b.length octets.

int read(byte[] b, int off, int len)

Lit dans un tableau doctets. La mthode read renvoie le nombre rel doctets lus ou 1 si la fin
du flux est atteinte.
Paramtres :

Tableau dans lequel sont lues les donnes.

off

Position dans le tableau partir de laquelle les premiers


octets seront placs.

len

Nombre maximal doctets lire.

long skip(long n)

Saute n octets du flux dentre. Renvoie le nombre doctets effectivement sauts (qui peut donc
tre infrieur n si la fin du flux est rencontre).

int available()

Renvoie le nombre doctets disponibles sans blocage (un blocage implique que le thread courant
passe son tour).

Livre Java .book Page 696 Jeudi, 25. novembre 2004 3:04 15

696

Au cur de Java 2 - Notions fondamentales

void close()

Ferme le flux dentre.

void mark(int readlimit)

Place un marqueur la position courante dans le flux dentre (tous les flux ne supportent pas
cette fonctionnalit). Quand le nombre doctets lus partir du flux dentre dpasse readlimit,
le marqueur disparat du flux.

void reset()

Revient au dernier marqueur. Les appels ultrieurs read relisent les mmes octets. Sil nexiste
pas de marqueur courant, le flux nest pas rinitialis.

boolean markSupported()

Renvoie true si le flux accepte le marquage.


java.io.OutputStream 1.0

abstract void write(int b)

Ecrit un octet de donnes.

void write(byte[] b)

Ecrit tous les octets du tableau b.

void write(byte[] b, int off, int len)

Ecrit une plage doctets du tableau b.


Paramtres :

Tableau source des donnes.

off

Dplacement dans ce tableau du premier octet crire.

len

Nombre doctets crire.

void close()

Vide entirement le tampon de sortie et ferme le flux.

void flush()

Vide entirement le flux de sortie, et plus prcisment envoie vers la cible les donnes se trouvant
dans un tampon.

La faune des flux


A la diffrence de C, qui sen sort trs bien avec son unique type de fichiers FILE*, ou de VB, qui en
possde trois, Java na pas moins de soixante types diffrents de flux qui forment une vraie faune !
(voir Figures 12.1 et 12.2). Les concepteurs de la bibliothque prtendent que ce nombre important
de types de flux vite certaines erreurs de programmation. Par exemple, en C, on peut penser quune
erreur banale consiste essayer dcrire dans un fichier uniquement ouvert en entre (erreur qui
nest, en fait, pas si banale). Naturellement, dans ce cas, la sortie est ignore lexcution. En Java
comme en C++, le compilateur trouve ce type derreur puisque ni un InputStream en Java ni un
istream en C++ ne possdent de mthode de sortie.

Livre Java .book Page 697 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

697

InputStream

Audio
InputStream

Buffered
InputStream

Checked
InputStream

Cipher
InputStream

ByteArray
InputStream

File
InputStream

Piped
InputStream

Filter
InputStream

Sequence
InputStream

StringBuffer
InputStream

Object
InputStream

Digest
InputStream

Inflater
InputStream

LineNumber
InputStream

Progress
Monitor
InputStream

Pushback
InputStream

Data
InputStream

<<interface>>
ObjectInput

GZIP
InputStream

Zip
InputStream

<<interface>>
DataInput
Random
AccessFile
<<interface>>
DataOutput

Jar
InputStream

<<interface>>
ObjectOutput

OutputStream

Buffered
OutputStream

ByteArray
OutputStream

File
OutputStream

Filter
OutputStream

Piped
OutputStream

Object
OutputStream

Checked
OutputStream

Cipher
OutputStream

Digest
OutputStream

Deflater
OutputStream

PrintStream

GZIP
OutputStream

Data
OutputStream

Zip
OutputStream

Jar
OutputStream

Figure 12.1
La hirarchie des E/S des flux.

On peut se demander si, en C++ et plus encore en Java, larme absolue contre les erreurs de
programmation ne serait pas en ralit lpouvante qui saisit les programmeurs figs devant la
complexit incroyable des bibliothques pour les flux.
INFO C++
Le C++ ANSI offre dj trop de types de flux avec, entre autres, ifstream, ofstream, fstream, wistream,
wifstream, istrstream, etc. (au total dix-huit classes). Mais Java dpasse vraiment les bornes en sparant les classes
lorsquil y a ou non buffrisation, anticipation, accs direct ou donnes binaires.

Livre Java .book Page 698 Jeudi, 25. novembre 2004 3:04 15

698

Au cur de Java 2 - Notions fondamentales

Reader

Buffered
Reader

CharArray
Reader

LineNumber
Reader

FilterReader

InputStream
Reader

Pushback
Reader

FileReader

PipedReader

StringReader

PipedWriter

PrintWriter

Writer

Buffered
Writer

CharArray
Writer

FilterWriter

OutputStream
Writer

StringWriter

FileWriter

Figure 12.2
Les hirarchies de Reader et de Writer.

Sparons les reprsentants de la faune des flux selon leur utilit. Quatre classes abstraites sont lorigine
de la faune : InputStream, OutputStream, Reader et Writer. On ne peut pas instancier ces types, mais
dautres mthodes peuvent les renvoyer. Par exemple (voir Chapitre 10), la classe URL possde une
mthode openStream qui renvoie un InputStream. On utilise ensuite un objet InputStream pour lire
partir de lURL. Nous avons vu que les classes InputStream et OutputStream ne permettent que de lire
et dcrire des octets un par un ou des tableaux doctets ; elles ne possdent pas de mthode pour lire ou
crire des chanes ou des nombres. Des sous-classes plus puissantes sont ncessaires. Par exemple,
DataInputStream et DataOutputStream permettent de lire et dcrire tous les types de base de Java.
Dautre part, pour le texte Unicode, nous avons vu quil existe des sous-classes de Reader et de
Writer. Les mthodes de base de ces deux classes sont comparables celles dInputStream et
dOutputStream.
abstract int read()
abstract void write(int b)

Livre Java .book Page 699 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

699

Elles oprent exactement comme les mthodes correspondantes des classes InputStream et
OutputStream, sauf bien entendu que la mthode read renvoie soit une unit de code Unicode (sous
forme dun entier entre 0 et 65 535), soit 1 si la fin du fichier est atteinte.
Enfin, il existe des flux trs utiles, par exemple ZipInputStream et ZipOutputStream qui permettent
de lire et dcrire des fichiers dans le format de compression bien connu ZIP.
De plus, le JDK 5.0 introduit quatre nouvelles interfaces : Closeable, Flushable, Readable et Appendable (voir Figure 12.3). Les deux premires interfaces sont trs simples, avec, respectivement, les
mthodes
void close() throws IOException

et
void flush()

Les classes InputStream, OutputStream, Reader et Writer implmentent toute linterface


Closeable. OutputStream et Writer implmentent linterface Flushable.
Linterface Readable dispose dune seule mthode :
int read(CharBuffer cb)

La classe CharBuffer possde des mthodes pour laccs en lecture/criture squentiel et alatoire.
Elle reprsente un tampon en mmoire ou un fichier concordance de mmoire.
Linterface Appendable possde deux mthodes pour annexer des caractres uniques et des suites de
caractres :
Appendable append(char c)
Appendable append(CharSequence s)

Le type CharSequence est une autre interface dcrivant les proprits minimales dune suite de
valeurs char. Il est implment par String, CharBuffer et StringBuilder/StringBuffer.
Parmi les classes de la faune des flux, seul Writer implmente Appendable.
java.io.Closeable 5.0
void close()

Ferme ce Closeable. Cette mthode peut dclencher une exception IOException.


java.io.Flushable 5.0
void flush()

Vide ce Flushable.
java.lang.Readable 5.0
int read(CharBuffer cb)

Tente de lire autant de valeurs char dans cb quil peut en contenir. Renvoie le nombre de valeurs
lues ou 1 si aucune autre valeur nest disponible partir de ce Readable.
java.lang.Appendable 5.0
Appendable append(char c)

Annexe lunit de code c cet Appendable; renvoie this.

Appendable append(CharSequence cs)

Annexe toutes les units de code de cs cet Appendable; renvoie this.

Livre Java .book Page 700 Jeudi, 25. novembre 2004 3:04 15

700

Au cur de Java 2 - Notions fondamentales

java.lang.CharSequence 1.4

char charAt(int index)

Renvoie lunit de code lindice donn.

int length()

Renvoie le nombre dunits de code de cette suite.

CharSequence subSequence(int startIndex, int endIndex)

Renvoie un CharSequence constitu des units de code stockes lindice startIndex jusqu
endIndex -1.

String toString()

Renvoie une chane constitue des units de code de cette suite.

Empilements de flux filtrs


FileInputStream et FileOutputStream fournissent des flux dentre et de sortie associs un
fichier sur disque. Il faut donner le nom du fichier ou le chemin complet du fichier dans le constructeur.
Ainsi,
FileInputStream fin = new FileInputStream("employee.dat");

va rechercher dans le rpertoire courant un fichier appel "employee.dat".


ATTENTION
Comme lantislash est le caractre dchappement dans les chanes en Java, il faut utiliser \\ pour crire des chemins
de fichiers dans le style Windows, par exemple C:\\Windows\\win.ini. Sous Windows on peut utiliser aussi un
slash comme dans C:/Windows/win.ini, car la plupart des appels au systme de fichiers de Windows savent interprter la barre oblique comme sparateur pour les fichiers. Cette pratique nest pas recommande, car le comportement des fonctions systme de Windows peut changer et, pour dautres systmes dexploitation, le sparateur pour
les fichiers peut tre diffrent. Si lon veut des programmes portables, il faut utiliser le sparateur correct, qui est
donn par la chane constante File.separator.

On peut aussi utiliser un objet File (voir plus loin dans ce chapitre) :
File f = new File("employee.dat");
FileInputStream fin = new FileInputStream(f);

Comme les classes abstraites InputStream et OutputStream, ces classes ne sont capables que de
lire ou dcrire des octets. Nous lisons donc des tableaux doctets partir de lobjet fin:
byte b = (byte)fin.read();

ASTUCE
Il peut tre utile de connatre le rpertoire courant de lutilisateur puisque toutes les classes de java.io interprtent
les chemins relatifs partir de ce dernier : cette information est obtenue par un appel System.getProperty("user.dir").

Livre Java .book Page 701 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

701

Nous verrons plus loin que DataInputStream ne permet que de lire les types numriques :
DataInputStream din = . . .;
double s = din.readDouble();

Mais, de mme que FileInputStream ne possde pas de mthode pour lire les types numriques,
DataInputStream na pas de mthode pour accder aux donnes dun fichier.
Java a recours un mcanisme astucieux pour sparer les deux rles. Certains flux (comme
FileInputStream et le flux dentre renvoy par la mthode openStream de la classe URL) peuvent
accder aux octets se trouvant dans les fichiers et dautres endroits plus exotiques. Dautres flux
(comme DataInputStream et PrintWriter) savent assembler les octets en des types de donnes
plus utiles. En Java, il relve de la responsabilit du programmeur de combiner les deux flux en ce
que lon appelle souvent des flux filtrs, plus prcisment en passant un flux existant au constructeur
dun autre flux. Par exemple, pour lire des nombres dans un fichier, on commencera par crer un
FileInputStream que lon passera ensuite au constructeur dun DataInputStream:
FileInputStream fin = new FileInputStream("employee.dat");
DataInputStream din = new DataInputStream(fin);
double s = din.readDouble();

Bien entendu, le flux dentre cr par ce code ne correspond pas un nouveau fichier sur disque. Le
flux ainsi obtenu continue accder aux donnes du fichier associ au flux dentre, mais nous
disposons maintenant dune interface plus puissante.
La Figure 12.1 montre les classes FilterInputStream et FilterOutputStream.
Vous pouvez combiner leurs sous-classes pour construire les flux dont vous avez besoin. Par exemple, par dfaut, les flux ne sont pas buffriss. Cela implique un appel systme pour chaque octet lu
ou crit. Supposons que vous vouliez la fois la buffrisation et les mthodes dentre de type
donne partir du fichier employee.dat se trouvant dans le rpertoire courant. Il vous faudra la
"monstrueuse" squence de constructeurs :
DataInputStream din = new DataInputStream(
new BufferedInputStream(
new FileInputStream("employee.dat")));

DataInputStream se trouve tre le dernier dans la chane des constructeurs parce que nous
voulons disposer des mthodes de DataInputStream et que celles-ci doivent elles-mmes utiliser la
mthode buffrise read. Malgr sa grande laideur, ce type de codage est incontournable : vous
devez continuer empiler des constructeurs de flux jusqu obtenir les fonctionnalits voulues.
Il peut aussi tre parfois ncessaire de garder une trace des flux intermdiaires du chanage. Par
exemple, en entre, on a souvent besoin de tester la valeur de loctet suivant. Java fournit cet effet
PushbackInputStream:
PushbackInputStream pbin = new PushbackInputStream(
new BufferedInputStream(
new FileInputStream("employee.dat")));

Parcourez tout hasard loctet suivant,


int b = pbin.read();

Livre Java .book Page 702 Jeudi, 25. novembre 2004 3:04 15

702

Au cur de Java 2 - Notions fondamentales

quitte le renvoyer sa place sil ne correspond pas votre attente :


if (b!= <) pbin.unread(b);

Le problme est que read et unread sont les seules mthodes applicables ce type de flux dentre.
Si vous voulez la fois anticiper sur la lecture et lire des nombres, il vous faut un flux dentre du
type prcdent et un flux dentre de donnes :
DataInputStream din = new DataInputStream(
pbin = new PushbackInputStream(
new BufferedInputStream(
new FileInputStream("employee.dat"))));

Tous les autres langages de programmation offrent dans leur bibliothque, pour les flux, des moyens
transparents pour la buffrisation, lanticipation et autres plaisanteries quelque peu assommantes
traiter en Java en empilant des flux filtrs. Cependant, la possibilit de combiner des classes de filtres
permet de construire une incroyable varit de squences de flux effectivement utilisables. Par exemple, si le problme est de lire des nombres se trouvant dans un fichier ZIP, on utilisera la squence
suivante de flux (voir Figure 12.4) :
ZipInputStream zin
= new ZipInputStream(new FileInputStream("employee.zip"));
DataInputStream din = new DataInputStream(zin);

Figure 12.4
Data
InputStream

Une squence
de flux filtrs.

Zip
InputStream

File
InputStream

read
read
read

(Voir la section sur les "flux de fichiers ZIP" dans ce chapitre pour plus de dtails sur les traitements
de fichiers ZIP Java.)
En dfinitive, si lon passe sur les "monstrueux" constructeurs ncessaires lempilement des flux,
pouvoir combiner les flux en Java est une fonctionnalit trs agrable !
java.io.FileInputStream 1.0

FileInputStream(String name)

Cre un nouveau flux de fichiers en entre, pour le fichier dont le chemin est pass dans la chane
name.

Livre Java .book Page 703 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

703

FileInputStream(File f)

Cre un nouveau flux de fichiers en entre, partir de linformation encapsule dans lobjet File
(la classe File est dcrite la fin du chapitre).
java.io.FileOutputStream 1.0

FileOutputStream(String name)

Cre un nouveau flux de fichiers en sortie spcifi par la chane name. Les chemins qui ne sont
pas absolus sont considrs comme relatifs au rpertoire courant. Attention : un fichier existant
portant le mme nom sera automatiquement dtruit.

FileOutputStream(String name, boolean append)

Cre un nouveau flux de fichiers en sortie spcifi par la chane name. Les chemins qui ne sont
pas absolus sont considrs comme relatifs au rpertoire courant. Si le paramtre append est
true, les donnes sont places la fin du fichier. Un fichier existant possdant le mme nom ne
sera pas cras dans ce seul cas.

FileOutputStream(File f)

Cre un nouveau flux de fichiers en sortie, partir de linformation encapsule dans lobjet File
(la classe File est dcrite la fin du chapitre). Attention : un fichier existant portant le mme
nom sera automatiquement dtruit.
java.io.BufferedInputStream 1.0

BufferedInputStream(InputStream in)

Cre un nouveau flux buffris avec une taille de tampon par dfaut. Un flux dentres buffrises lit des caractres partir dun flux sans avoir accder chaque caractre depuis le dispositif source des donnes. Quand le tampon est vide, il y a lecture dun nouveau bloc de donnes
qui est plac dans le tampon.

BufferedInputStream(InputStream in, int n)

Cre un nouveau flux buffris avec une taille de tampon dfinie par lutilisateur.
java.io.BufferedOutputStream 1.0

BufferedOutputStream(OutputStream out)

Cre un nouveau flux buffris avec une taille de tampon par dfaut. Un flux dentres buffrises accepte des caractres devant tre crits sans avoir accder chaque caractre depuis le
dispositif source des donnes. Quand le tampon est plein ou sur un ordre de vidage du flux, les
donnes du tampon sont crites.

BufferedOutputStream(OutputStream out, int n)

Cre un nouveau flux buffris avec une taille de tampon dfinie par lutilisateur.
java.io.PushbackInputStream 1.0

PushbackInputStream(InputStream in)

Construit un flux avec anticipation sur un caractre.

PushbackInputStream(InputStream in, int size)

Construit un flux avec un tampon danticipation de la taille spcifie par size.

Livre Java .book Page 704 Jeudi, 25. novembre 2004 3:04 15

704

Au cur de Java 2 - Notions fondamentales

void unread(int b)

Renvoie un caractre dans le flux dentre. Ce caractre sera relu par un appel read ultrieur.
On doit renvoyer les octets un par un.
Paramtres :

Le caractre qui sera relu.

Flux de donnes
Il est gnralement ncessaire dcrire ou de relire le rsultat dun calcul. Les flux de donnes possdent des mthodes pour lire tous les types de base de Java. Voici les mthodes de linterface
DataOutput pour crire un nombre, un caractre ou une valeur boolenne :
writeChars
writeByte
writeInt
writeShort
writeLong
writeFloat
writeDouble
writeChar
writeBoolean
writeUTF

Par exemple, writeInt crit toujours un entier sur quatre octets, quel que soit le nombre de chiffres,
et writeDouble crit toujours un double sur huit octets. Les sorties binaires qui en rsultent ne sont
videmment pas directement lisibles par lhomme, mais lespace ncessaire en mmoire ou sur
disque sera le mme pour une valeur dun type donn, optimisant ainsi les entres/sorties (voir, dans
ce chapitre, la classe PrintWriter qui permet dcrire des nombres sous une forme comprhensible
par lhomme).
INFO
Il existe deux mthodes pour stocker en mmoire des entiers ou des nombres virgule flottante, selon la plateforme utilise. Prenons le cas dune valeur numrique int sur quatre octets, par exemple le nombre dcimal
1234 ou 4D2 en hexadcimal (1234 = 4 256 + 13 16 + 2). Elle peut tre stocke de faon que les quatre
premiers octets de la mmoire contiennent les octets de poids fort (MSB) de la valeur : 00 00 04 D2. Cest
la mthode de stockage dite big-endian. Nous pouvons aussi commencer avec les octets de poids faible (LSB)
mthode appele little-endian. Sparc utilise la mthode big-endian alors que Pentium utilise little-endian. Cela
peut poser des problmes. Quand on sauvegarde un fichier en C ou en C++, les donnes sont crites exactement
comme le processeur les a stockes en mmoire. Cela rend difficile le transfert des fichiers, mme comprenant
des donnes trs simples, dune plate-forme une autre. En Java, toutes les valeurs sont crites selon la mthode
big-endian, quel que soit le processeur. Les fichiers de donnes Java sont par consquent indpendants de la
plate-forme.

La mthode writeUTF crit les donnes des chanes en utilisant une version modifie au format UTF
8 bits (Unicode Transformation Format). Au lieu de simplement utiliser le codage standard UTF-8
(prsent au Tableau 12.1), les chanes de caractres sont dabord reprsentes au format UTF-16
(voir Tableau 12.2), puis le rsultat est encod laide des rgles UTF-8. Lencodage modifi diffre
pour les caractres ayant un code suprieur 0xFFFF. Il est utilis pour une compatibilit en amont
avec les machines virtuelles qui ont t construites lorsque Unicode nallait pas encore au-del de
16 bits.

Livre Java .book Page 705 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

705

Personne dautre nutilisant UTF-8 modifi, nemployez la mthode writeUTF que pour crire des
chanes destines une machine virtuelle Java, par exemple si vous crivez un programme qui
gnre des bytecodes. Vous utiliserez la mthode writeChars dans les autres cas.
Tableau 12.1 : Codage UTF-8

Plage de caractres

Codage

07F

0a6a5a4a3a2a1a0

807FF

110a10a9a8a7a6 10a5a4a3a2a1a0

800FFFF

1110a15a14a13a12 10a11a10a9a8a7a6 10a5a4a3a2a1a0

1000010FFFF

11110a20a19a18 10a17a16a15a14a13a12 10a11a10a9a8a7a6


10a5a4a3a2a1a0

Tableau 12.2 : Codage UTF-16

Plage de caractres

Codage

0FFFF

a15a14a13a12a11a10a9a8 a7a6a5a4a3a2a1a0

100010FFFF

110110b19b18 b17a16a15a14a13a12a11a10 110111a9a8b


a7a6a5a4a3a2a1a0 o b19b18b17b16 = a20a19a18a17a16 -1

INFO
Voyez RFC 2279 (http://ietf.org/rfc/rfc2279.txt) et RFC 2781 (http://ietf.org/rfc/rfc2781.txt) pour obtenir les dfinitions
dUTF-8 et dUTF-16.

Pour relire les donnes, on utilisera les mthodes suivantes :


readInt

readDouble

readShort

readChar

readLong

readBoolean

readFloat

readUTF

INFO
Le format de donnes binaires est compact et indpendant des plates-formes. Il convient bien laccs direct, sauf
pour les chanes UTF. Le seul inconvnient majeur des fichiers binaires est que lil humain ne peut les lire.

java.io.DataInput 1.0

boolean readBoolean()

Lit une valeur boolenne.

Livre Java .book Page 706 Jeudi, 25. novembre 2004 3:04 15

706

Au cur de Java 2 - Notions fondamentales

byte readByte()

Lit un octet (8 bits).

char readChar()

Lit un caractre Unicode de 16 bits.

double readDouble()

Lit un type double de 64 bits.

float readFloat()

Lit un nombre flottant sur 32 bits.

void readFully(byte[] b)

Lit des octets et attend que tous les octets soient lus.
Paramtres :

Dsigne le tampon dans lequel les donnes sont lues.

void readFully(byte[] b, int off, int len)

Lit les octets dans le tableau b, bloquant tout jusqu ce que tous les octets soient lus.
Paramtres :

Dsigne le tampon dans lequel les donnes sont lues.

off

Dplacement pour le dbut des donnes.

len

Nombre maximal doctets qui seront lus.

int readInt()

Lit un entier de 32 bits.

String readLine()

Lit une ligne termine par \n, \r, \r\n ou EOF. Renvoie une chane contenant tous les octets de
la ligne convertis en caractres Unicode.

long readLong()

Lit un entier long de 64 bits.

short readShort()

Lit un entier court de 16 bits.

String readUTF()

Lit une chane de caractres au format UTF-8 modifi.

int skipBytes(int n)

Saute n octets et reste en attente jusqu ce que tous les octets soient sauts.
Paramtres :

java.io.DataOutput 1.0

void writeBoolean(boolean b)

Ecrit une valeur boolenne.

void writeByte(int b)

Ecrit un octet sur 8 bits.

Nombre doctets sauter.

Livre Java .book Page 707 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

707

void writeChar(int c)

Ecrit un caractre Unicode sur 16 bits.

void writeChars(String s)

Ecrit tous les caractres de la chane.

void writeDouble(double d)

Ecrit un type double sur 64 bits.

void writeFloat(float f)

Ecrit un nombre flottant sur 32 bits.

void writeInt(int i)

Ecrit un entier sur 32 bits.

void writeLong(long l)

Ecrit un entier long sur 64 bits.

void writeShort(int s)

Ecrit un entier court sur 16 bits.

void writeUTF(String s)

Ecrit une chane de caractres au format UTF-8 modifi.

Flux de fichiers en accs direct


La classe de flux RandomAccessFile permet de chercher ou dcrire des donnes depuis nimporte
quel emplacement dun fichier. Elle implmente les deux interfaces DataInput et DataOutput. Les
fichiers sur disque sont en accs direct, mais les flux de donnes provenant dun rseau ne le sont
pas. On ouvre un fichier en accs direct soit en lecture seule, soit en lecture-criture. Cette option est
indique par la chane "r" (en lecture seule) ou "rw" (en lecture-criture ou read-write), que lon
passe au constructeur comme second argument :
RandomAccessFile in = new RandomAccessFile("employee.dat", "r");
RandomAccessFile inOut
= new RandomAccessFile("employee.dat", "rw");

Un fichier existant ouvert en accs direct en tant que RandomAccessFile nest pas cras.
Un fichier en accs direct possde, de plus, un pointeur de fichier qui indique constamment la position de lenregistrement suivant (celui qui sera lu ou crit). La mthode seek place le pointeur de
fichier sur un octet ayant une position arbitraire dans le fichier. Largument pass seek est un entier
long, compris entre zro et la longueur du fichier exprime en octets.
La mthode getFilePointer renvoie la position courante du pointeur de fichier.
Pour lire dans un fichier en accs direct, on utilise les mmes mthodes comme readInt ou
readChar que pour les objets DataInputStream. Ce nest pas un hasard : ces mthodes sont en
fait dfinies dans linterface DataInput quimplmentent DataInputStream ainsi que RandomAccessFile.
De mme, pour crire dans un fichier en accs direct, on utilise les mmes mthodes writeInt ou
writeChar que pour la classe DataOutputStream. Ces mthodes sont dfinies dans linterface
DataOutput commune aux deux classes.

Livre Java .book Page 708 Jeudi, 25. novembre 2004 3:04 15

708

Au cur de Java 2 - Notions fondamentales

Lintrt de la classe RandomAccessFile est quelle implmente simultanment DataInput et


DataOutput, ce qui permet dutiliser des mthodes (pour lire ou crire) dont les types darguments
sont ceux des interfaces DataInput et DataOutput:
class Employee
{ . . .
read(DataInput in) { . . . }
write(DataOutput out) { . . . }
}

On remarque que la mthode read peut aussi bien traiter un objet DataInputStream ou un objet
RandomAccessFile puisque chacune de ces classes implmente linterface DataInput. Il en va de
mme pour la mthode write.
java.io.RandomAccessFile 1.0

RandomAccessFile(String file, String mode)

Paramtres :

Nom de fichier du systme dexploitation.

mode

"r" en lecture seule ou "rw" en lecture-criture.

RandomAccessFile(File file, String mode)

Paramtres :

name

file

Objet File encapsulant un nom de fichier dpendant


du systme dexploitation (la classe File est dcrite
la fin du chapitre).

mode

"r" en lecture seule ou "rw" en lecture-criture.

long getFilePointer()

Renvoie la position courante du pointeur de fichier.

void seek(long pos)

Fixe la position du pointeur de fichier pos (nombre doctets partir du dbut du fichier).

long length()

Renvoie la longueur du fichier en octets.

Les flux de texte


Nous venons de voir les entres/sorties binaires. Rptons-le : si les entres/sorties binaires sont
rapides et efficaces, elles ne sont pas faites pour lil humain, linverse des entres/sorties texte
que nous allons maintenant examiner. Par exemple lentier 1234 est reprsent en binaire (en notation hexadcimale) comme la squence doctets 000004 D2. En format texte, ce serait une chane
"1234".
Hlas, pour faire cela en Java, il faut un peu de travail. Nous savons que Java se sert des caractres
Unicode : le codage en caractres de la chane "1234" en est fait (en notation hexadcimale)
0031003200330034. La plupart des environnements possdent aujourdhui leur propre
systme de codage de caractres. Ce schma de codage peut utiliser un octet, deux octets, ou mme
un nombre variable doctets. Par exemple, sous Windows, la chane prcdente sera en ASCII
31323334, sans les octets zro. Si un codage Unicode est crit vers un fichier texte, il est trs
improbable que le fichier obtenu reste lisible en utilisant les outils de lenvironnement de la machine
hte. Pour contourner ce problme, Java comprend tout un ensemble de flux filtrs qui permettent de
passer les chanes en codage Unicode aux diffrents codages de caractres des systmes dexploitation.

Livre Java .book Page 709 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

709

Toutes ces classes descendent des classes abstraites Reader et Writer, et leurs noms sont calqus sur
ceux que nous venons de voir. Ainsi la classe InputStreamReader transforme un flux dentres
contenant des octets dans un codage particulier en un lecteur mettant des caractres Unicode.
A linverse, la classe OutputStreamWriter transforme un flux de caractres Unicode en un flux
doctets dans un codage particulier de type caractres.
Voici, par exemple, comment instancier un lecteur dentres pour saisir des frappes clavier et les
convertir automatiquement en Unicode :
InputStreamReader in = new InputStreamReader(System.in);

Ce lecteur de flux dentres utilise par dfaut le jeu de caractres normal du systme hte. Par exemple, sous Windows, ce sera le codage ISO 8859-1 (encore appel ISO Latin-1 ou, pour les programmeurs Windows, "code ANSI"). On peut choisir un codage diffrent en le spcifiant au constructeur
dInputStreamReader:
InputStreamReader(InputStream, String)

o la chane dcrit le systme de codage utiliser. Par exemple,


InputStreamReader in = new InputStreamReader(
new FileInputStream("kremlin.dat"), "ISO8859_5");

La prochaine section dtaille plus avant les jeux de caractres.


Il est trs frquent dassocier un objet reader ou un objet writer un fichier, et deux classes ont t
cres pour faciliter lopration, FileReader et FileWriter. Par exemple, la dfinition de writer
FileWriter out = new FileWriter("output.txt");

peut remplacer :
FileWriter out = new FileWriter(new
FileOutputStream("output.txt"));

Jeux de caractres
Auparavant, les jeux de caractres internationaux taient grs de manire assez htrogne dans
la bibliothque Java. Le package java.nio (introduit dans le JDK 1.4) a permis dunifier la
conversion des jeux de caractres avec lintroduction de la classe Charset (attention, le s est en
minuscule).
Un jeu de caractres met en correspondance des suites dunits de code Unicode sur deux octets et
des suites doctets utilises dans le codage de caractres local. Lun des codages de caractres les
plus populaires est ISO-8859-1, qui code sur un octet les 256 premiers caractres Unicode. Le ISO8859-15 gagne en importance, il remplace certains des caractres les moins utiles du jeu ISO-88591 avec les lettres accentues en franais et finnois et, surtout, il remplace le caractre de "devise
internationale" par le symbole de leuro dans le point de code 0xA4. Autres exemples de codages
de caractres, les codages octets variables utiliss pour le japonais et le chinois.
La classe Charset utilise les noms des jeux de caractres standardiss dans le Registre des jeux
de caractres de lIANA (http://www.iana.org/assignments/character-sets). Ces noms diffrent
lgrement de ceux utiliss dans les versions prcdentes. A titre dexemple, le nom "officiel" du jeu
ISO-8859-1 est dsormais "ISO-8859-1" et non plus "ISO8859_1", qui tait le nom utilis jusquau

Livre Java .book Page 710 Jeudi, 25. novembre 2004 3:04 15

710

Au cur de Java 2 - Notions fondamentales

JDK 1.3. Pour des raisons de compatibilit avec dautres conventions de dnomination, chaque jeu
de caractres peut avoir plusieurs alias. ISO-8859-1, par exemple, a les alias suivants :
ISO8859-1
ISO_8859_1
ISO8859_1
ISO_8859-1
ISO_8859-1:1987
8859_1
latin1
l1
csISOLatin1
iso-ir-100
cp819
IBM819
IBM-819
819

La casse nest pas importante dans les noms des jeux de caractres.
Pour obtenir un Charset, appelez la mthode statique forName avec le nom officiel ou lun de ses
alias :
Charset cset = Charset.forName("ISO-8859-1");

La mthode aliases renvoie un objet Set des alias. Un Set est une collection que nous verrons au
Volume 2 ; voici le code pour parcourir les lments du jeu :
Set<String> aliases = cset.aliases();
for (String alias: aliases)
System.out.println(alias);

INFO
Une excellente rfrence dcrivant la "soupe de lettres ISO 8859" se trouve ladresse http://czyborra.com/charsets/
iso8859.html.

Les versions internationales de Java prennent en charge de nombreux codages supplmentaires.


Il existe mme un mcanisme pour ajouter des fournisseurs de jeux de caractres supplmentaires
(voir la documentation du JDK pour en savoir plus). Pour dcouvrir les jeux de caractres disponibles dans une implmentation particulire, appelez la mthode statique availableCharsets. Elle
renvoie un SortedMap, une autre classe de collection. Utilisez ce code pour dcouvrir les noms de
tous les jeux de caractres disponibles :
Set<String, Charset> charsets = Charset.availableCharsets();
for (String name: charsets.keySet())
System.out.println(name);

Le Tableau 12.3 recense les codages de caractres ncessaires toute implmentation Java. Quant au
Tableau 12.4, il recense les schmas de codage que le JDK installe par dfaut. Les jeux de caractres
des Tableaux 12.5 et 12.6 ne sont installs que sur les systmes dexploitation utilisant des langues
non europennes. Les schmas de codage du Tableau 12.6 sont fournis des fins de compatibilit
avec les versions prcdentes du JDK.
Les schmas de codage locaux ne peuvent pas reprsenter tous les caractres Unicode. Lorsquun
caractre ne peut pas tre reprsent, il est transform en ?.

Livre Java .book Page 711 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

711

Tableau 12.3 : Codages des caractres ncessaires

Nom standard
du Charset

Nom existant

Description

US-ASCII

ASCII

American Standard Code for Information Interchange

ISO_8859-1

ISO8859_1

ISO 8859-1, alphabet latin n 1

UTF-8

UTF8

Norme de transformation Unicode des caractres


sur 8 bits

UTF-16

UTF-16

Norme de transformation Unicode des caractres


sur 16 bits, ordre des octets spcifi par une
marque obligatoire dordre initial des octets

UTF-16BE

UnicodeBigUnmarked

Norme de transformation Unicode des caractres


sur 16 bits, octets ordonns "big-endian"

UTF-16LE

UnicodeLittle Unmarked

Norme de transformation Unicode des caractres


sur 16 bits, octets ordonns "little-endian"

Tableau 12.4 : Codages de caractres de base

Nom standard
du Charset

Nom existant

Description

ISO8859_2

ISO8859_2

ISO 8859-2, alphabet latin n 2

ISO8859_4

ISO8859_4

ISO 8859-4, alphabet latin n 4

ISO8859_5

ISO8859_5

ISO 8859-5, alphabet latin/cyrillique

ISO8859_7

ISO8859_7

ISO 8859-7, alphabet latin/grec

ISO8859_9

ISO8859_9

ISO 8859-9, alphabet latin n 5

ISO8859_13

ISO8859_13

ISO 8859-13, alphabet latin n 7

ISO8859_15

ISO8859_15

ISO 8859-15, alphabet latin n 9

windows-1250

Cp1250

Windows Europe de lEst

windows-1251

Cp1251

Windows cyrillique

windows-1252

Cp1252

Windows latin-1

windows-1253

Cp1253

Windows grec

windows-1254

Cp1254

Windows turc

windows-1257

Cp1257

Windows baltique

Livre Java .book Page 712 Jeudi, 25. novembre 2004 3:04 15

712

Au cur de Java 2 - Notions fondamentales

Tableau 12.5 : Codages de caractres tendus

Nom standard
du Charset

Nom existant

Description

Big5

Big5

Big5, chinois traditionnel

Big5-HKSCS

Big5-HKSCS

Big5, extensions pour Hong Kong, chinois traditionnel

EUC-JP

EUC_JP

JIS X 0201, 0208, 0212, codage EUC, japonais

EUC-KR

EUC_KR

KS C 5601, codage EUC, coren

GB18030

GB18030

Chinois simplifi, PRC standard

GBK

GBK

GBK, chinois simplifi

ISCII91

ISCII91

ISCII91, codage du script indien

ISO-2022-JP

ISO2022JP

JIS X 0201, 0208 dans la forme ISO 2022, japonais

ISO-2022-KR

ISO2022KR

ISO 2022 KR, coren

ISO8859-3

ISO8859_3

ISO 8859-3, alphabet latin n 3

ISO8859-6

ISO8859_6

ISO 8859-6, alphabet latin/arabe

ISO8859-8

ISO8859_8

ISO 8859-8, alphabet latin/hbreu

Shift_JIS

SJIS

Japonais, Shift-JIS

TIS-620

TIS620

Tha, TIS620

windows-1255

Cp1255

Windows hbreu

windows-1256

Cp1256

Windows arabe

windows-1258

Cp1258

Windows vietnamien

windows-31j

MS932

Japonais pour Windows

x-EUC-CN

EUC_CN

GB2312, codage EUC, chinois simplifi

x-EUC-JP-LINUX

EUC_JP_LINUX

JIS X 0201, 0208, codage EUC, japonais

x-EUC-TW

EUC_TW

CNS11643 (plane 1-3), codage EUC, chinois traditionnel

x-MS950-HKSCS

MS950_HKSCS

Chinois traditionnel pour Windows, avec extension pour


Hong Kong

x-mswin-936

MS936

Chinois simplifi pour Windows

x-windows949

MS949

Coren pour Windows

x-windows-950

MS950

Chinois traditionnel pour Windows

Livre Java .book Page 713 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

713

Tableau 12.6 : Codages de caractres existants

Nom existant

Description

Cp037

Anglais amricain (Etats-Unis), canadien (anglais, franais), hollandais,


portugais, brsilien et australien

Cp273

Autrichien, allemand pour IBM

Cp277

Danois, norvgien pour IBM

Cp278

Finnois, sudois pour IBM

Cp280

Italien pour IBM

Cp284

Catalan espagnol, espagnol (Amrique du Sud) pour IBM

Cp285

Anglais (Grande-Bretagne et Irlande) pour IBM

Cp297

Franais pour IBM

Cp420

Arabe pour IBM

Cp424

Hbreu pour IBM

Cp437

MS-DOS anglais Etats-Unis, Australie, Nouvelle-Zlande, Afrique du


Sud

Cp500

EBCDIC 500V1

Cp737

Grec pour PC

Cp775

Balte pour PC

Cp838

Tha tendu SBCS pour IBM

Cp850

MS-DOS latin-1

Cp852

MS-DOS latin-2

Cp855

Cyrillique pour IBM

Cp856

Hbreu pour IBM

Cp857

Turc pour IBM

Cp858

Variante de Cp850 avec le caractre euro

Cp860

MS-DOS portugais

Cp861

MS-DOS islandais

Cp862

Hbreu pour PC

Livre Java .book Page 714 Jeudi, 25. novembre 2004 3:04 15

714

Au cur de Java 2 - Notions fondamentales

Tableau 12.6 : Codages de caractres existants (suite)

Nom existant

Description

Cp863

MS-DOS canadien franais

Cp864

Arabe pour PC

Cp865

MS-DOS nordique

Cp866

MS-DOS russe

Cp868

MS-DOS pakistanais

Cp869

Grec moderne pour IBM

Cp870

IBM Multilingue latin-2

Cp871

Islandais pour IBM

Cp874

Tha pour IBM

Cp875

Grec pour IBM

Cp918

Pakistanais (urdu) pour IBM

Cp921

Letton, lituanien pour IBM (AIX, DOS)

Cp922

Estonien pour IBM (AIX, DOS)

Cp930

Japonais katakana kanji avec 4370 UDC, extension de 5026

Cp933

Coren avec 1880 UDC, extension de 5029

Cp935

Chinois simplifi hte avec 1880 UDC, extension de 5031

Cp937

Chinois traditionnel hte avec 6204 UDC, extension de 5033

Cp939

Japonais latin kanji avec 4370 UDC, extension de 5035

Cp942

Japonais (IBM OS/2), extension de Cp932

Cp942C

Variante de Cp942

Cp943

Japonais OS/2, extension de Cp932 et Shift-JIS pour IBM

Cp943C

Variante de Cp943

Cp948

Chinois OS/2 (Tawan) extension de 938

Cp949

Coren pour PC

Cp949C

Variante de Cp949

Cp950

Chinois pour PC (Hong Kong, Tawan)

Livre Java .book Page 715 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

Tableau 12.6 : Codages de caractres existants (suite)

Nom existant

Description

Cp964

Chinois pour AIX IBM (Tawan)

Cp970

Coren pour AIX IBM

Cp1006

Pakistanais (urdu) pour AIX IBM

Cp1025

Cyrillique multilingue pour IBM : Bulgarie, Bosnie-Herzgovine,


Macdoine (FYR)

Cp1026

Turc pour IBM latin-5

Cp1046

Arabe pour IBM - Windows

Cp1097

Iranien (farsi)/Perse pour IBM

Cp1098

Iranien (farsi)/Perse (PC) pour IBM

Cp1112

Letton, lituanien pour IBM

Cp1122

Estonien pour IBM

Cp1123

Ukrainien pour IBM

Cp1124

Ukrainien pour IBM AIX

Cp1140

Variante de Cp037 avec le caractre euro

Cp1141

Variante de Cp273 avec le caractre euro

Cp1142

Variante de Cp277 avec le caractre euro

Cp1143

Variante de Cp278 avec le caractre euro

Cp1144

Variante de Cp280 avec le caractre euro

Cp1145

Variante de Cp284 avec le caractre euro

Cp1146

Variante de Cp285 avec le caractre euro

Cp1147

Variante de Cp297 avec le caractre euro

Cp1148

Variante de Cp500 avec le caractre euro

Cp1149

Variante de Cp871 avec le caractre euro

Cp1381

IBM OS/2, DOS Rpublique populaire de Chine

Cp1383

IBM AIX Rpublique populaire de Chine

Cp33722

IBM-eucJP - japonais (extension de 5050)

715

Livre Java .book Page 716 Jeudi, 25. novembre 2004 3:04 15

716

Au cur de Java 2 - Notions fondamentales

Tableau 12.6 : Codages de caractres existants (suite)

Nom existant

Description

ISO2022CN

ISO 2022 CN, chinois (conversion en Unicode seulement)

ISO2022CN_CNS

CNS 11643 dans la forme ISO 2022 CN, chinois traditionnel (conversion
en Unicode seulement)

ISO2022CN_GB

GB 2312 dans la forme ISO 2022 CN, chinois simplifi (conversion en


Unicode seulement)

JIS0201

JIS X 0201, japonais

JIS0208

JIS X 0208, japonais

JIS0212

JIS X 0212, japonais

JISAutoDetect

Dtecte et convertit de Shift-JIS, EUC-JP, ISO 2022 JP (conversion en


Unicode seulement)

Johab

Johab, coren

MS874

Tha pour Windows

MacArabic

Arabe pour Macintosh

MacCentralEurope

Latin-2 pour Macintosh

MacCroatian

Croate pour Macintosh

MacCyrillic

Cyrillique pour Macintosh

MacDingbat

Police Dingbat pour Macintosh

MacGreek

Grec pour Macintosh

MacHebrew

Hbreu pour Macintosh

MacIceland

Islandais pour Macintosh

MacRoman

Police Roman pour Macintosh

MacRomania

Roumain pour Macintosh

MacSymbol

Police Symbol pour Macintosh

MacThai

Tha pour Macintosh

MacTurkish

Turc pour Macintosh

MacUkraine

Ukrainien pour Macintosh

Livre Java .book Page 717 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

717

Lorsque vous disposez dun jeu de caractres, vous pouvez lutiliser pour convertir des chanes
Unicode et des suites doctets encodes. Voici comment coder une chane Unicode :
String str = . . .;
ByteBuffer buffer = cset.encode(str);
byte[] bytes = buffer.array();

A linverse, pour dcoder une suite doctets, il vous faut un tampon (buffer) doctets. Utilisez la
mthode statique wrap du tableau ByteBuffer pour transformer un tableau doctets en un tampon
doctets. Le rsultat de la mthode decode est un CharBuffer. Appelez sa mthode toString pour
obtenir une chane :
byte[] bytes = . . .;
ByteBuffer bbuf = ByteBuffer.wrap(bytes, offset, length);
CharBuffer cbuf = cset.decode(bbuf);
String str = cbuf.toString();
java.nio.charset.Charset 1.4

static SortedMap availableCharsets()

Rcupre tous les jeux de caractres disponibles pour cette machine virtuelle. Renvoie une
correspondance dont les cls sont des noms de jeux de caractres et les valeurs, des jeux de
caractres.

static Charset forName(String name)

Rcupre un jeu de caractres pour le nom donn.

Set aliases()

Renvoie le jeu de noms dalias pour ce jeu de caractres.

ByteBuffer encode(String str)

Code la chane donne en une suite doctets.

CharBuffer decode(ByteBuffer buffer)

Dcode la suite de caractres donne. Les entres non reconnues sont converties en "caractres
de remplacement" Unicode ("\uFFFD").
java.nio.ByteBuffer 1.4

byte[] array()

Renvoie le tableau des octets gr par ce tampon.

static ByteBuffer wrap(byte[] bytes)


static ByteBuffer wrap(byte[] bytes, int offset, int length)

Renvoie un tampon doctets qui gre le tableau doctets donn ou la plage donne.
java.nio.charBuffer

char[] array()

Renvoie le tableau des units de code gr par ce tampon.

char charAt(int index)

Renvoie lunit de code lindice donn.

String toString()

Renvoie une chane constitue des units de code, gre par ce tampon.

Livre Java .book Page 718 Jeudi, 25. novembre 2004 3:04 15

718

Au cur de Java 2 - Notions fondamentales

La sortie du texte
Pour sortir du texte, on peut utiliser lobjet PrintWriter. Cet outil permet dafficher des chanes et
des nombres au format texte. A linstar de DataOutputStream qui contient des mthodes agrables,
mais qui ne peut spcifier une destination, un objet PrintWriter doit tre associ un objet Writer
vers une destination :
PrintWriter out = new PrintWriter(new
FileWriter("employee.txt"));

On aurait pu aussi lassocier un flux de sortie spcifiant la destination :


PrintWriter out = new PrintWriter(new
FileOutputStream("employee.txt"));

Le constructeur PrintWriter(OutputStream) ajoute automatiquement un OutputStreamWriter


pour convertir dans le flux les caractres Unicode en octets.
Pour crire sur un objet PrintWriter, on utilisera les mmes mthodes print et println que pour
System.out. Ces mthodes peuvent afficher des nombres (int, short, long, float, double), des
caractres, des valeurs boolennes, des chanes et des objets.
INFO
Si vous connaissez Java depuis son origine, vous vous demandez peut-tre ce que sont devenus PrintStream et
System.out. En Java 1.0, la classe PrintStream se contentait de tronquer les caractres Unicode pour en faire des
caractres ASCII (en enlevant loctet de poids fort). A linverse, la mthode readLine de DataInputStream convertissait les caractres ASCII en Unicode en forant loctet de poids fort 0. Il est bien vident que cette approche nest
ni propre ni portable. Java 2 a rsolu le problme en introduisant les objets reader et writer. Pour rester compatibles avec le code existant, System.in, System.out et System.err demeurent des flux et ne sont pas transforms en objets reader ou writer. Dsormais, la classe PrintStream convertit de faon interne les caractres
Unicode dans le codage par dfaut de lordinateur hte comme le PrintWriter. Les objets de type PrintStream
se comportent exactement comme des objets PrintWriter en ce qui concerne les mthodes print et println,
mais contrairement ces derniers, ils sont capables de sortir des octets non structurs par les mthodes write(int)
et write(byte[]).

Par exemple,
String name = "Harry Hacker ";
double salary = 75000;
out.print(name);
out.print( );
out.println(salary);

crit les caractres


Harry Hacker 35000

dans le flux out. Ces caractres sont ensuite convertis en octets et parviennent enfin au fichier
employee.txt.
La mthode println ajoute automatiquement la ligne les caractres de fin de ligne qui conviennent au systme cible ("\r\n" pour Windows, "\n" pour UNIX ou "\r" pour Macintosh). Ces chanes
sont renvoyes par lappel System.getProperty("line.separator").

Livre Java .book Page 719 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

719

Si lobjet writer fonctionne en mode de vidage automatique [autoflush], tous les caractres du
tampon sont envoys vers leur destination chaque appel de println (les objets PrintWriter sont
toujours buffriss). Par dfaut, le mode de vidage automatique nest pas activ. Pour activer ou
dsactiver le vidage automatique, il faut passer au constructeur PrintWriter(Writer, boolean) la
valeur boolenne approprie dans le second argument :
PrintWriter out = new PrintWriter(new
FileWriter("employee.txt"), true); // vidage automatique

Les mthodes print ne lancent pas dexception. La mthode checkError permet de savoir sil sest
produit un problme avec le flux.
INFO
On ne peut pas envoyer du binaire un objet PrintWriter. Il ne sait traiter que des sorties au format texte.

java.io.PrintWriter 1.1

PrintWriter(Writer out)

Cre un nouveau PrintWriter, sans vidage automatique de la ligne.


Paramtres :

out

Objet writer de sortie de caractres.

PrintWriter(Writer out, boolean autoFlush)

Cre un nouveau PrintWriter.


Paramtres :

out

Objet writer de sortie de caractres.

autoFlush

Si ce paramtre est true, les mthodes println() vident le


tampon de sortie.

PrintWriter(OutputStream out)

Cre un nouveau PrintWriter, sans vidage automatique de la ligne, partir dun objet existant
OutputStream en crant automatiquement lobjet intermdiaire ncessaire OutputStreamWriter.
Paramtres :

out

Flux de sortie.

PrintWriter(OutputStream out, boolean autoFlush)

Cre aussi un nouveau PrintWriter partir dun objet existant OutputStream, mais permet de
plus de spcifier si lobjet writer fera ou non un vidage automatique de la ligne.
Paramtres :

out

Flux de sortie.

autoFlushSi ce paramtre est true, les mthodes println() vident le tampon de sortie.

void print(Object obj)

Affiche un objet partir de la chane provenant de toString.


Paramtres :

obj

Lobjet afficher.

void print(String s)

Affiche une chane Unicode.

void println(String s)

Affiche une chane suivie par une fin de ligne. Vide le flux dans le cas o le mode de vidage automatique est activ.

Livre Java .book Page 720 Jeudi, 25. novembre 2004 3:04 15

720

Au cur de Java 2 - Notions fondamentales

void print(char[] s)

Affiche un tableau de caractres Unicode.

void print(char c)

Affiche un caractre Unicode.

void print(int i)

Affiche un entier au format texte.

void print(long l)

Affiche un entier long au format texte.

void print(float f)

Affiche un nombre virgule flottante au format texte.

void print(double d)

Affiche un nombre virgule flottante en double prcision au format texte.

void print(boolean b)

Affiche une valeur boolenne au format texte.

boolean checkError()

Renvoie true sil se produit une erreur de formatage ou une erreur de sortie. Si une erreur se
produit, le flux devient corrompu et tous les appels checkError renvoient true.

Lentre de texte
Nous savons dj quil faut utiliser :
m

DataOutputStream pour crire des donnes au format binaire ;

PrintWriter pour crire au format texte.

Ainsi, nous pouvons penser lgitimement quil existe une classe permettant de lire les donnes au
format texte symtrique de DataInputStream. La symtrie la plus proche est la classe Scanner,
que nous avons largement utilise. Pour traiter le texte en entre, on ne peut que recourir aux possibilits de lobjet BufferedReader, dont la mthode readLine permet de lire une ligne de texte. Il va
falloir lassocier une source en entre :
BufferedReader in = new BufferedReader(new
FileReader("employee.txt"));

La mthode readLine renvoie null lorsquil ne reste plus dentres. Voici une boucle dentre :
String s;
while ((s = in.readLine())!= null)
{
ici placer le traitement de s;
}

La classe FileReader a dj converti les octets en caractres Unicode. Pour les autres sources en
entre, il faut utiliser InputStreamReader. A la diffrence de PrintWriter, lobjet InputStreamReader ne possde pas de mthode remplaant automatiquement les caractres Unicode par
des octets :
BufferedReader in2 = new BufferedReader(new
InputStreamReader(System.in));

Livre Java .book Page 721 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

721

BufferedReader in3 = new BufferedReader(new


InputStreamReader(url.openStream()));

Pour lire des nombres partir dune entre de texte, il faut dabord lire une chane, puis la convertir :
String s = in.readLine();
double x = Double.parseDouble(s);

Ce qui prcde est correct sil ny a quun seul nombre par ligne. Dans le cas contraire, il faut
travailler un peu plus et dcouper la chane en entre en utilisant par exemple les utilitaires de la
classe StringTokenizer. Nous donnons un exemple un peu plus loin.
ASTUCE
Java possde des classes StringReader et StringWriter pour traiter une chane exactement comme un flux de
donnes, ce qui peut se rvler trs pratique puisque le mme code peut analyser des chanes et des donnes se trouvant
dans un flux.

Les flux de fichiers ZIP


Les fichiers ZIP sont des archives contenant un ou plusieurs fichiers dans un format en principe
compress. Java 2 accepte les formats GZIP et ZIP (voir RFC 1950, RFC 1951 et RFC 1952, par
exemple sur le site http://www.faqs.org/rfcs). Nous ne traitons ici que le format ZIP plus frquemment rencontr (bien quun peu plus complexe) et vous laissons tudier les classes GZIP si vous en
avez lutilit (elles fonctionnent de manire assez comparable).
INFO
Les classes destines aux fichiers ZIP se trouvent dans java.util.zip et non dans java.io. Il ne faut donc pas
oublier dajouter une instruction import indispensable. Les classes GZIP et ZIP sont cependant des sous-classes de
java.io.FilterInputStream et de java.io.FilterOutputStream. Les packages java.util.zip contiennent aussi des classes pour calculer des sommes de contrles CRC (Cyclic Redundancy Check ou contrle cyclique par
redondance ; cest une mthode produisant un code comparable un code de hachage que le destinataire dun
fichier va utiliser pour contrler si des erreurs sont apparues dans les donnes au cours de la transmission).

Un fichier ZIP possde un en-tte comprenant un certain nombre dinformations comme le nom du
fichier et la mthode de compression utilise. Nous lisons en Java un fichier ZIP laide de lobjet
ZipInputStream en empilant le constructeur ZipInputStream sur un FileInputStream. Nous
pouvons alors accder chaque entre individuelle de larchive. La mthode getNextEntry renvoie
un objet de type ZipEntry qui dcrit lentre. La mthode read de ZipInputStream a t modifie
pour renvoyer 1, non pas la fin du fichier ZIP, mais la fin de lentre courante. Il est, par consquent, obligatoire dappeler closeEntry pour pouvoir passer lentre suivante. Voici un exemple
de lecture dans un fichier ZIP :
ZipInputStream zin = new ZipInputStream
(new FileInputStream(zipname));
ZipEntry entry;
while ((entry = zin.getNextEntry())!= null)
{
ici analyser entry;

Livre Java .book Page 722 Jeudi, 25. novembre 2004 3:04 15

722

Au cur de Java 2 - Notions fondamentales

lire le contenu de zin;


zin.closeEntry();
}
zin.close();

Pour lire le contenu dune entre ZIP, on vitera de se servir de la mthode read sans formatage.
On se servira en principe des mthodes dun flux filtr mieux appropri. Par exemple, pour lire un
fichier texte se trouvant dans un fichier ZIP :
BufferedReader in = new BufferedReader
(new InputStreamReader(zin));
String s;
while ((s = in.readLine())!= null)
ici placer le traitement de s;

Le programme de lExemple 12.1 ouvre un fichier ZIP, puis affiche les fichiers sy trouvant dans une
liste combine en bas de lcran. En double-cliquant sur lun des fichiers, son contenu saffiche dans
la zone de texte (voir Figure 12.4).
Exemple 12.1 : ZipTest.java
import
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.io.*;
java.util.*;
java.util.zip.*;
javax.swing.*;
javax.swing.filechooser.FileFilter;

public class ZipTest


{
public static void main(String[] args)
{
ZipTestFrame frame = new ZipTestFrame();
frame.setTitle("ZipTest");
frame.setSize(300, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}
/**
Un cadre contenant une zone de texte pour afficher le contenu dun
fichier dans une archive zip, une liste combine pour choisir
diffrents fichiers de larchive et un menu pour charger une nouvelle
archive.
*/
class ZipTestFrame extends JFrame
{
public ZipTestFrame()
{
// ajouter le menu et les lments Open et Exit
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");

Livre Java .book Page 723 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

JMenuItem openItem = new JMenuItem("Open");


menu.add(openItem);
openItem.addActionListener(new OpenAction());
JMenuItem exitItem = new JMenuItem("Exit");
menu.add(exitItem);
exitItem.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
menuBar.add(menu);
setJMenuBar(menuBar);
// ajouter la zone de texte et la liste droulante
fileText = new JTextArea();
fileCombo = new JComboBox();
fileCombo.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
loadZipFile((String)fileCombo.getSelectedItem());
}
});
Container contentPane = getContentPane();
contentPane.add(fileCombo, BorderLayout.SOUTH);
contentPane.add(fileText, BorderLayout.CENTER);
}
/**
Voici lcouteur du menu File->Open.
*/
private class OpenAction implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
// Demande un fichier zip lutilisateur
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
ExtensionFileFilter filter = new ExtensionFileFilter();
filter.addExtension(".zip");
filter.addExtension(".jar");
filter.setDescription("ZIP archives");
chooser.setFileFilter(filter);
int r = chooser.showOpenDialog(ZipTestFrame.this);
if (r == JFileChooser.APPROVE_OPTION)
{
zipname = chooser.getSelectedFile().getPath();
scanZipFile();
}
}
}

723

Livre Java .book Page 724 Jeudi, 25. novembre 2004 3:04 15

724

Au cur de Java 2 - Notions fondamentales

/**
Analyse le contenu de larchive zip et remplit
la liste combine.
*/
public void scanZipFile()
{
fileCombo.removeAllItems();
try
{
ZipInputStream zin = new ZipInputStream(new
FileInputStream(zipname));
ZipEntry entry;
while ((entry = zin.getNextEntry())!= null)
{
fileCombo.addItem(entry.getName());
zin.closeEntry();
}
zin.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
/**
Charge un fichier depuis larchive zip dans la zone de texte
@param name Le nom du fichier dans larchive
*/
public void loadZipFile(String name)
{
try
{
ZipInputStream zin = new ZipInputStream(new
FileInputStream(zipname));
ZipEntry entry;
fileText.setText("");
// retrouver lentre ayant le nom correspondant dans larchive
while ((entry = zin.getNextEntry())!= null)
{
if (entry.getName().equals(name))
{
// lire lentre dans la zone de texte
BufferedReader in = new BufferedReader(new
InputStreamReader(zin));
String line;
while ((line = in.readLine())!= null)
{
fileText.append(line);
fileText.append("\n");
}
}
zin.closeEntry();
}
zin.close();
}
catch (IOException e)
{

Livre Java .book Page 725 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

e.printStackTrace();
}
}
private JComboBox fileCombo;
private JTextArea fileText;
private String zipname;
}
/**
Ce filtre de fichier correspond tous les fichiers avec un jeu
dextensions donn. De FileChooserTest au Chapitre9
*/
class ExtensionFileFilter extends FileFilter
{
/**
Ajoute une extension que ce filtre de fichiers reconnat.
@param extension Une extension de fichier (comme ".txt" ou "txt")
*/
public void addExtension(String extension)
{
if (!extension.startsWith("."))
extension = "." + extension;
extensions.add(extension.toLowerCase());
}
/**
Dfinit une description pour le jeu de fichiers reconnu par
ce filtre de fichiers.
@param aDescription Une description pour le jeu de fichiers
*/
public void setDescription(String aDescription)
{
description = aDescription;
}
/**
Renvoie une description du jeu de fichiers reconnu par
ce filtre de fichiers.
@return Une description pour le jeu de fichiers
*/
public String getDescription()
{
return description;
}
public boolean accept(File f)
{
if (f.isDirectory()) return true;
String name = f.getName().toLowerCase();
// vrifier si le nom du fichier se termine par lune des
// extensions
for (int i = 0; i < extensions.size(); i++)
if (name.endsWith((String)extensions.get(i)))

725

Livre Java .book Page 726 Jeudi, 25. novembre 2004 3:04 15

726

Au cur de Java 2 - Notions fondamentales

return true;
return false;
}
private String description = "";
private ArrayList extensions = new ArrayList();
}

Figure 12.5
Le programme ZipTest.

INFO
Le flux dentre ZIP lance une exception ZipException quand il se produit une erreur de lecture au niveau dun
fichier ZIP. Cette erreur se produit normalement quand le fichier ZIP est altr.

Pour crire un fichier ZIP, il faut ouvrir un flux ZipOutputStream en lempilant sur un FileOutputStream. Un objet ZipEntry doit tre cr pour chacune des entres futures du fichier ZIP.
Il suffit de passer le nom du fichier au constructeur ZipEntry, qui dterminera les autres paramtres,
comme la date de cration du fichier et la mthode de dcompression par dfaut. Il est possible de
modifier ces paramtres si ncessaire. Il faut ensuite appeler la mthode putNextEntry du flux
ZipOutputStream pour commencer crire dans un nouveau fichier. Les donnes du fichier sont
envoyes dans le flux ZIP, aprs quoi il faut appeler closeEntry. Tout cela est rpter pour chacun
des fichiers que lon veut archiver. Voici un squelette de programme :
FileOutputStream fout = new FileOutputStream("test.zip");
ZipOutputStream zout = new ZipOutputStream(fout);
pour tous les fichiers
{
ZipEntry ze = new ZipEntry(nom du fichier);
zout.putNextEntry(ze);

Livre Java .book Page 727 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

727

envoyer les donnes zout;


zout.closeEntry();
}
zout.close();

INFO
Les fichiers JAR (voir Chapitre 10) sont tout simplement des fichiers ZIP possdant une entre appele manifeste. On peut
lire le manifeste avec les classes JarInputStream et JarOutputStream.

Les fichiers ZIP illustrent bien la puissance dabstraction du flux. Aussi bien la source que la destination de donnes ZIP sont totalement modifiables. Vous empilez lobjet reader le mieux appropri
sur un flux de fichiers ZIP pour lire les donnes se trouvant sous une forme compresse. Lobjet
reader ne sait pas que les donnes sont dcompresses lorsquon lactive. De plus, la source
doctets au format ZIP nest pas ncessairement un fichier : les donnes ZIP peuvent provenir dune
connexion rseau. De mme, lorsque le chargeur de classes dun applet lit un fichier JAR, il lit et
dcompresse des donnes provenant du rseau.
INFO
Larticle situ ladresse http://www.javaworld.com/javaworld/jw-10-2000/jw-1027-toolbox.html montre comment
modifier une archive ZIP.

java.util.zip.ZipInputStream 1.1

ZipInputStream(InputStream in)

Cre un ZipInputStream qui permet de dcompresser les donnes se trouvant dans le flux
InputStream pass en paramtre.
Paramtres :

in

Flux dentre sous-jacent.

ZipEntry getNextEntry()

Renvoie un objet ZipEntry pour lentre suivante ou null sil nen existe plus.

void closeEntry()

Ferme lentre courante du fichier ZIP. Il est alors possible de lire lentre suivante en se servant
de getNextEntry().
java.util.zip.ZipOutputStream 1.1

ZipOutputStream(OutputStream out)

Cre un ZipOutputStream permettant dcrire des donnes compresses dans le flux spcifi
OutputStream.
Paramtres :

out

Flux de sortie sous-jacent.

void putNextEntry(ZipEntry ze)

Ecrit linformation dans le flux ZipEntry et prpare le flux accepter les donnes. Les donnes
seront crites dans le flux par write().
Paramtres :

ze

Nouvelle entre.

Livre Java .book Page 728 Jeudi, 25. novembre 2004 3:04 15

728

Au cur de Java 2 - Notions fondamentales

void closeEntry()

Ferme lentre courante du fichier ZIP. On se servira de la mthode putNextEntry() pour


passer lentre suivante.

void setLevel(int level)

Fixe le niveau de compression par dfaut des entres ultrieures DEFLATED (valeur par dfaut :
Deflater.DEFAULT_COMPRESSION). Lance une exception IllegalArgumentException si la
valeur du niveau nest pas correcte.
Paramtres :

level

Niveau de compression, entre 0 (NO_COMPRESSION),


pas de compression, et 9 (BEST_COMPRESSION),
compression maximale.

void setMethod(int method)

Spcifie la mthode de compression par dfaut pour le flux ZipOutputStream courant, et ce


pour toute entre ne spcifiant pas de mthode.
Paramtres :

method

Mthode de compression : DEFLATED ou STORED.

java.util.zip.ZipEntry 1.1

ZipEntry(String name)

Paramtres :

name

Nom de lentre.

long getCrc()

Renvoie la valeur de contrle CRC32 pour la ZipEntry traite.

String getName()

Renvoie le nom de lentre traite.

long getSize()

Renvoie la taille non compresse de lentre traite ou 1 si cette taille nest pas connue.

boolean isDirectory()

Renvoie un boolen pour prciser si lentre traite est un rpertoire.

void setMethod(int method)

Paramtres :

method

Mthode de compression pour lentre traite.


Ne peut tre que DEFLATED ou STORED.

void setSize(long size)

Fixe la taille de lentre. Seulement ncessaire pour la mthode de compression STORED.


Paramtres :

size

Taille non compresse de lentre.

void setCrc(long crc)

Fixe la somme de contrle CRC32 de cette entre. La classe CRC32 permet de calculer la somme
de contrle. Seulement ncessaire pour la mthode de compression STORED.
Paramtres :

crc

Somme des contrles de lentre.

Livre Java .book Page 729 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

729

java.util.zip.ZipFile 1.1

ZipFile(String name)

Le constructeur courant cre un ZipFile pour lire partir de la source spcifie.


Paramtres :

name

Chane contenant le chemin du fichier source.

ZipFile(File file)

Le constructeur courant cre un ZipFile pour lire partir de lobjet spcifi File.
Paramtres :

file

Fichier lire. La classe File est dcrite la fin du chapitre.

Enumeration entries()

Renvoie un objet Enumeration donnant les objets ZipEntry qui dcrivent les entres du
ZipFile.

ZipEntry getEntry(String name)

Renvoie lentre dont le nom est pass en paramtre ou null sil nexiste pas dentre de ce nom.
Paramtres :

name

Nom de lentre.

InputStream getInputStream(ZipEntry ze)

Renvoie un InputStream pour une entre donne.


Paramtres :

ze

Une entre ZipEntry dans le fichier ZIP courant.

String getName()

Renvoie le chemin du fichier ZIP trait.

Lutilisation des flux


Dans les quatre parties qui suivent, nous verrons comment faire bon usage de certaines cratures de
notre faune de flux. Dans tous nos exemples, nous nous servirons de la classe Employee et de certaines sous-classes comme Manager (voir Chapitres 4 et 5). Nous allons traiter quatre scnarios diffrents pour sauvegarder un tableau denregistrements demploys dans un fichier, puis pour les relire
en mmoire :
1. Sauvegarder des donnes qui soient toutes du mme type (Employee) au format texte.
2. Sauvegarder des donnes du mme type au format binaire.
3. Sauvegarder et relire des donnes polymorphes (un mlange dobjets Employee et Manager).
4. Sauvegarder et relire des donnes contenant des rfrences imbriques (des instances de Manager pointant vers dautres Employee).

Ecrire en format fixe


Nous allons voir comment sauvegarder un tableau denregistrements Employee en format fixe bien
connu. Chaque enregistrement occupe une ligne distincte. Les champs de chaque instance sont spars les uns des autres par des dlimiteurs. Notre dlimiteur sera la barre verticale (|). On aurait pu
choisir le deux-points (:) (on apprciera le fait que chaque programmeur puisse choisir un dlimiteur diffrent). Nous parions bien sr quil ny aura pas de barre verticale | dans les chanes que nous
traiterons.

Livre Java .book Page 730 Jeudi, 25. novembre 2004 3:04 15

730

Au cur de Java 2 - Notions fondamentales

INFO
Nous avons vu surtout sous UNIX une quantit incroyable de fichiers stocks exactement dans ce format. On trouve
ainsi des bases de donnes du personnel contenant des milliers denregistrements au format texte avec aucun autre
outil dinterrogation que les utilitaires dUNIX awk, sort et join ( loppos, dans le monde PC, o des bases de
donnes excellentes sont accessibles un faible cot, ce type de stockage "bricol" est beaucoup plus rare).

Voici des exemples denregistrements :


Harry Hacker|35000|1989|10|1
Carl Cracker|75000|1987|12|15
Tony Tester|38000|1990|3|15

Ecrire les enregistrements ne pose aucun problme. Puisque nous crivons dans un fichier texte,
nous utilisons la classe PrintWriter. Nous crivons simplement chaque champ suivi par une barre
verticale |. Seul le dernier champ est suivi de \n. De plus, pour rester cohrent avec le concept
de classe, qui doit tre capable daccepter un certain nombre de messages, nous ajoutons une
mthode writeData notre classe Employee:
public void writeData(PrintWriter out) throws IOException
{
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(hireDay);
out.println(name+"|"
+salary+"|"
+calendar.get(Calendar.YEAR)+ "|"
+(calendar.get(Calendar.MONTH)+1)+"|"
+calendar.get(Calendar.DAY_OF_MONTH));
}

Nous allons lire les enregistrements ligne par ligne et sparer les champs. Cest le sujet que nous allons
maintenant traiter, avec laide dune classe fournie dans les utilitaires de Java pour nous simplifier le
travail.

Analyseurs lexicaux pour les textes dlimits


Quand on lit une ligne en entre, on obtient une longue ligne dune seule pice quil va falloir dcouper en chanes distinctes. Cela implique de reprer les dlimiteurs | puis de procder au dcoupage
en squences de caractres allant jusquaux dlimiteurs suivants (ces lments ainsi obtenus sont
appels des tokens). La classe StringTokenizer de java.util a t conue dans le but doffrir une
mthode rapide pour dcouper une vaste chane contenant du texte dlimit : cest un analyseur lexical. Lide de base est de lier un objet instance de lanalyseur lexical une chane. Quand on
construit cet objet, on spcifie les dlimiteurs. Par exemple, dans notre cas, nous utiliserons
StringTokenizer tokenizer = new StringTokenizer(line, "|");

On peut spcifier plusieurs dlimiteurs dans la chane, par exemple


StringTokenizer tokenizer = new StringTokenizer(line, "|,;");

Ainsi, tout caractre de la chane peut servir de dlimiteur.


Si vous ne spcifiez pas de jeu de dlimiteurs, le paramtre par dfaut est " \t\n\r", savoir tous
les caractres despace vide (espace, tabulation, nouvelle ligne et retour chariot).

Livre Java .book Page 731 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

731

Une fois lanalyseur syntaxique construit, ces mthodes vont permettre dextraire efficacement les tokens
de la chane. La mthode nextToken renvoie le token suivant se trouvant encore dans le flux. La mthode
hasMoreTokens renvoie true sil reste encore des tokens. La boucle suivante traite tous les tokens :
while (tokenizer.hasMoreTokens())
{
String token = tokenizer.nextToken();
code de traitement du token
}

INFO
Une alternative StringTokenizer est la mthode split de la classe String. Lappel line.split("[|,;]")
renvoie un tableau String[] constitu de tous les tokens et utilisant les dlimiteurs figurant entre crochets. Vous
pouvez utiliser toute expression ordinaire pour dcrire les dlimiteurs. Nous verrons les expressions ordinaires plus
loin dans ce chapitre.

java.util.StringTokenizer 1.0

StringTokenizer(String str, String delim)

Construit un analyseur syntaxique de chane avec le jeu de dlimiteurs spcifi.


Paramtres :

str

Chane dentre contenant les tokens.

delim

Chane contenant les dlimiteurs (chaque caractre de cette


chane est un dlimiteur).

StringTokenizer(String str)

Construit un analyseur syntaxique avec, par dfaut, lensemble de dlimiteurs "t\n\r".

boolean hasMoreTokens()

Renvoie true sil reste des tokens analyser.

String nextToken()

Renvoie le token suivant. Lance une exception NoSuchElementException sil ne reste plus de
tokens analyser.

String nextToken(String delim)

Renvoie le token suivant aprs passage un autre ensemble de dlimiteurs. Le nouvel ensemble
de dlimiteurs remplace le prcdent.

int countTokens()

Renvoie le nombre de tokens restant dans la chane.

Lecture en format fixe


La lecture dun enregistrement Employee ne pose pas de vrai problme. Il suffit de lire ligne par
ligne lentre avec la mthode readLine de la classe BufferedReader. Voici le code ncessaire la
lecture dun seul enregistrement :
BufferedReader in
= new BufferedReader(new FileReader("employee.dat"));
. . .
String line = in.readLine();

Livre Java .book Page 732 Jeudi, 25. novembre 2004 3:04 15

732

Au cur de Java 2 - Notions fondamentales

Il nous faut maintenant extraire les tokens. Cette opration nous fournira des chanes, quil nous
restera convertir en valeurs numriques.
Comme nous lavons fait avec la mthode writeData, nous ajoutons une mthode readData la
classe Employee. Ainsi par cet appel :
e.readData(in);

la mthode crase le contenu prcdent de e. On se souvient que la mthode lance une IOException
si la mthode readLine lance elle-mme cette exception. Cette mthode ne peut rien faire sil se
produit une IOException, sinon la propager vers le haut de la chane des appelants. Voici le code de
cette mthode :
public void readData(BufferedReader is) throws IOException
{
String s = in.readLine();
StringTokenizer t = new StringTokenizer(s, "|");
name = t.nextToken();
salary = Double.parseDouble(t.nextToken());
int y = Integer.parseInt(t.nextToken());
int m = Integer.parseInt(t.nextToken());
int d = Integer.parseInt(t.nextToken());
GregorianCalendar calendar
= new GregorianCalendar(y, m - 1, d);
// GregorianCalendar utilise 0 = janvier
hireDay = calendar.getTime();
}

Pour terminer, dans le code dun programme de test pour ces mthodes, la mthode statique
void writeData(Employee[] e, PrintWriter out)

commence par crire la longueur du tableau, puis chaque enregistrement. La mthode statique
Employee[] readData(BufferedReader in)

commence par lire la longueur du tableau, puis chaque enregistrement (voir Exemple 12.2).
Exemple 12.2 : DataFileTest.java
import java.io.*;
import java.util.*;
public class DataFileTest
{
public static void main(String[] args)
{
Employee[] staff = new Employee[3];
staff[0]
1987,
staff[1]
1989,
staff[2]
1990,

= new Employee("Carl Cracker", 75000,


12, 15);
= new Employee("Harry Hacker", 50000,
10, 1);
= new Employee("Tony Tester", 40000,
3, 15);

Livre Java .book Page 733 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

try
{
// enregistrer tous les dossiers employ dans le
// fichier employee.dat
PrintWriter out = new PrintWriter(new
FileWriter("employee.dat"));
writeData(staff, out);
out.close();
// rcuprer tous les enregistrements dans un nouveau tableau
BufferedReader in = new BufferedReader(new
FileReader("employee.dat"));
Employee[] newStaff = readData(in);
in.close();
// afficher les enregistrements employ nouvellement lus
for (int i = 0; i < newStaff.length; i++)
System.out.println(newStaff[i]);
}
catch(IOException exception)
{
exception.printStackTrace();
}
}
/**
Ecrit tous les employs dans un tableau vers un printWriter
@param e Un tableau demploys
@param out Un printWriter
*/
static void writeData(Employee[] e, PrintWriter out)
throws IOException
{
// crire le nombre demploys
out.println(e.length);
for (int i = 0; i < e.length; i++)
e[i].writeData(out);
}
/**
Lit un tableau demploys partir dun lecteur buffris
@param in Le lecteur buffris
@return Le tableau demploys
*/
static Employee[] readData(BufferedReader in)
throws IOException
{
// rcuprer la taille du tableau
int n = Integer.parseInt(in.readLine());
Employee[] e = new Employee[n];
for (int i = 0; i < n; i++)
{

733

Livre Java .book Page 734 Jeudi, 25. novembre 2004 3:04 15

734

Au cur de Java 2 - Notions fondamentales

e[i] = new Employee();


e[i].readData(in);
}
return e;
}
}
class Employee
{
public Employee() {}
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
// GregorianCalendar utilise 0 = janvier
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+ "]";
}
/**
Ecrit les donnes des employs dans un PrintWriter
@param out Le PrintWriter
*/
public void writeData(PrintWriter out) throws IOException
{
GregorianCalendar calendar = new GregorianCalendar();

Livre Java .book Page 735 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

735

calendar.setTime(hireDay);
out.println(name + "|"
+ salary + "|"
+ calendar.get(Calendar.YEAR) + "|"
+ (calendar.get(Calendar.MONTH) + 1) + "|"
+ calendar.get(Calendar.DAY_OF_MONTH));
}
/**
Lit les donnes des employs depuis un lecteur buffris
@param in Le lecteur buffris
*/
public void readData(BufferedReader in) throws IOException
{
String s = in.readLine();
StringTokenizer t = new StringTokenizer(s, "|");
name = t.nextToken();
salary = Double.parseDouble(t.nextToken());
int y = Integer.parseInt(t.nextToken());
int m = Integer.parseInt(t.nextToken());
int d = Integer.parseInt(t.nextToken());
GregorianCalendar calendar
= new GregorianCalendar(y, m - 1, d);
// GregorianCalendar utilise 0 = janvier
hireDay = calendar.getTime();
}
private String name;
private double salary;
private Date hireDay;
}

La classe StringBuilder
Pour le traitement dune saisie, vous devez souvent construire des chanes partir de caractres individuels ou dunits de code Unicode. La concatnation serait totalement inefficace dans ce cas. A
chaque annexion de caractres une chane, lobjet chane doit trouver une nouvelle mmoire pour
contenir la grande chane : lopration prend du temps. Lannexion dautres caractres implique que
la chane doive tre relocalise encore et encore. La classe StringBuilder permet dviter ce
problme.
StringBuilder fonctionne peu prs comme un ArrayList. Il gre un tableau char[] qui peut
crotre ou rduire la demande. Vous pouvez annexer, insrer ou supprimer des units de code
jusqu ce que le constructeur de chane contienne la chane souhaite. Vous utilisez ensuite la
mthode toString pour transformer le contenu en un objet String.
INFO
La classe StringBuilder a t introduite dans le JDK 5.0. Son prdcesseur, StringBuffer, est lgrement moins
efficace mais permet plusieurs threads dajouter ou de supprimer des caractres. Si toute la modification des chanes
survient dans un seul thread, utilisez plutt StringBuilder. Les API des deux classes sont identiques.

Livre Java .book Page 736 Jeudi, 25. novembre 2004 3:04 15

736

Au cur de Java 2 - Notions fondamentales

Les notes dAPI suivantes contiennent les mthodes les plus importantes pour les classes StringBuilder et StringBuffer.
java.lang.StringBuilder 5.0
java.lang.StringBuffer 1.0

StringBuilder/StringBuffer()

Construit un constructeur de chane vide ou un tampon de chane.

StringBuilder/StringBuffer(int length)

Construit un StringBuilder ou un StringBuffer vide avec la longueur de capacit initiale.

StringBuilder/StringBuffer(String str)

Construit un StringBuilder ou un StringBuffer avec le contenu initial str.

int length()

Renvoie le nombre dunits de code du constructeur ou du tampon.

StringBuilder/StringBuffer append(String str)

Annexe une chane et renvoie this.

StringBuilder/StringBuffer append(char c)

Annexe une unit de code et renvoie this.

StringBuilder/StringBuffer appendCodePoint(int cp) 5.0

Annexe un point de code, en le transformant en une ou deux units de code et renvoie this.

void setCharAt(int i, char c)

Dfinit la ime unit de code sur c.

StringBuilder/StringBuffer insert(int offset, String str)

Insre une chane la position offset et renvoie this.

StringBuilder/StringBuffer insert(int offset, char c)

Insre une unit de code la position offset et renvoie this.

StringBuilder/StringBuffer delete(int startIndex, int endIndex)

Supprime les units de code avec les dcalages startIndex endIndex - 1 et renvoie this.

String toString()

Renvoie une chane contenant les mmes donnes que le constructeur ou le tampon.

Les flux en accs direct


Si le nombre denregistrements dEmployee en longueur variable est important, nous allons rencontrer
un dfaut au niveau de la technique de stockage que nous venons de voir : il est impossible de lire un
enregistrement se trouvant au milieu du fichier sans lire au pralable tous les enregistrements qui le
prcdent. Supposons maintenant que tous les enregistrements aient la mme longueur. Cela nous
permet dimplmenter une mthode daccs direct pour lire linformation avec la mthode RandomAccessFile que nous avons vue, et grce cette mthode, le temps daccs aux enregistrements
devient identique.

Livre Java .book Page 737 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

737

Les valeurs numriques sont stockes au format binaire dans les champs des instances de nos classes
en utilisant les mthodes writeInt et writeDouble de linterface DataOutput (nous avons dj dit
quil sagit de linterface commune aux classes DataOutputStream et RandomAccessFile).
Pour que la taille de tous les enregistrements soit la mme, nous devons ajuster la taille de toutes les
chanes la mme valeur au moment o nous les stockons. Le format variable UTF ne convient pas,
et rien de pratique ne se trouve dans les bibliothques de Java pour y parvenir : nous allons tre obligs dcrire un peu de code implmentant deux mthodes utilitaires pour donner une longueur arbitraire aux chanes. Nous appelons ces mthodes capables dcrire et de lire des chanes Unicode dont
la longueur est impose, respectivement, writeFixedString et readFixedString.
La mthode writeFixedString crit le nombre de caractres spcifi par le paramtre size en
commenant par le dbut de la chane (si le nombre dunits de code est trop peu important, la
mthode la complte par des valeurs de zro). Voici son code :
static void writeFixedString
(String s, int size, DataOutput out)
throws IOException
{
int i;
for (i = 0; i < size; i++)
{
char ch = 0;
if (i < s.length()) ch = s.charAt(i);
out.writeChar(ch);
}
}

La mthode readFixedString lit dans le flux dentre size caractres, sauf rencontrer la valeur
0 : en ce cas, elle passe sur les valeurs nulles restantes du champ dentre.
Pour plus defficacit, la mthode utilise la classe StringBuilder pour lire vers une chane :
static String readFixedString(int size, DataInput in)
throws IOException
{
StringBuffer b = new StringBuffer(size);
int i = 0;
boolean more = true;
while (more && i < size)
{
char ch = in.readChar();
i++;
if (ch == 0) more = false;
else b.append(ch);
}
in.skipBytes(2 * (size - i));
return b.toString();
}

INFO
Nous avons plac les mthodes writeFixedString et readFixedString dans la classe utilitaire DataIO.

Livre Java .book Page 738 Jeudi, 25. novembre 2004 3:04 15

738

Au cur de Java 2 - Notions fondamentales

Pour crire un enregistrement de taille fixe, il faut tout simplement crire tous ses champs en
binaire :
public void writeData(DataOutput out) throws IOException
{
DataIO.writeFixedString(name, NAME_SIZE, out);
out.writeDouble(salary);
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(hireDay);
out.writeInt(calendar.get(Calendar.YEAR));
out.writeInt(calendar.get(Calendar.MONTH)+ 1);
out.writeInt(calendar.get(Calendar.DAY_OF_MONTH));
}

Relire linformation est tout aussi simple :


public void readData(DataInput in) throws IOException
{
name = DataIO.readFixedString(NAME_SIZE, in);
salary = in.readDouble();
int y = in.readInt();
int m = in.readInt();
int d = in.readInt();
GregorianCalendar calendar
= new GregorianCalendar(y, m - 1, d);
hireDay = calendar.getTime();
}

Dans notre exemple, les enregistrements des employs font tous 100 octets puisque nous avons
spcifi que le champ du nom aurait toujours 40 caractres. Do la dcomposition suivante de
lenregistrement :
40 caractres = 80 octets pour le nom
1 double = 8 octets
3 int = 12 octets
Pour positionner le pointeur de fichier sur le troisime enregistrement, il faut appeler la mthode
seek:
long n = 3;
int RECORD_SIZE = 100;
in.seek((n - 1) * RECORD_SIZE);

Pour lire un enregistrement, il faut alors crire :


Employee e = new Employee();
e.readData(in);

Pour rcrire un enregistrement modifi au mme endroit, il ne faut pas oublier de positionner
nouveau le pointeur de fichier au dbut de lenregistrement :
in.seek((n - 1) * RECORD.SIZE);
e.writeData(out);

Pour obtenir le nombre total doctets dans un fichier, utilisez la mthode length. Le nombre total
denregistrements revient la largeur divise par la taille de chaque enregistrement.
long int nbytes = in.length(); // longueur en octets
int nrecords = (int)(nbytes / Taille de lenregistrement

Livre Java .book Page 739 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

739

Le programme de test de lExemple 12.3 crit trois enregistrements dans un fichier de donnes, puis
les relit dans le fichier dans lordre inverse. Cela est fait efficacement avec laccs direct au fichier,
qui nous permet de commencer par le dernier enregistrement.
Exemple 12.3 : RandomFileTest.java
import java.io.*;
import java.util.*;
public class RandomFileTest
{
public static void main(String[] args)
{
Employee[] staff = new Employee[3];
staff[0]
1987,
staff[1]
1989,
staff[2]
1990,

= new Employee("Carl Cracker", 75000,


12, 15);
= new Employee("Harry Hacker", 50000,
10, 1);
= new Employee("Tony Tester", 40000,
3, 15);

try
{
// enregistrer tous les dossiers employ dans le
// fichier employee.dat
DataOutputStream out = new DataOutputStream(new
FileOutputStream("employee.dat"));
for (int i = 0; i < staff.length; i++)
staff[i].writeData(out);
out.close();
// rcuprer tous les enregistrements dans un nouveau tableau
RandomAccessFile in
= new RandomAccessFile("employee.dat", "r");
// calculer la taille du tableau
int n = (int)(in.length() / Employee.RECORD_SIZE);
Employee[] newStaff = new Employee[n];
// lire les employs dans lordre inverse
for (int i = n - 1; i >= 0; i--)
{
newStaff[i] = new Employee();
in.seek(i * Employee.RECORD_SIZE);
newStaff[i].readData(in);
}
in.close();
// afficher les enregistrements employ nouvellement lus
for (int i = 0; i < newStaff.length; i++)
System.out.println(newStaff[i]);
}
catch(IOException e)
{
e.printStackTrace();
}
}

Livre Java .book Page 740 Jeudi, 25. novembre 2004 3:04 15

740

Au cur de Java 2 - Notions fondamentales

}
class Employee
{
public Employee() {}
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
// GregorianCalendar utilise 0 = janvier
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+ "]";
}
/**
Ecrit les donnes des employs dans une sortie de donnes
@param out La sortie de donnes
*/
public void writeData(DataOutput out) throws IOException
{
DataIO.writeFixedString(name, NAME_SIZE, out);
out.writeDouble(salary);

Livre Java .book Page 741 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

GregorianCalendar calendar = new GregorianCalendar();


calendar.setTime(hireDay);
out.writeInt(calendar.get(Calendar.YEAR));
out.writeInt(calendar.get(Calendar.MONTH) + 1);
out.writeInt(calendar.get(Calendar.DAY_OF_MONTH));
}
/**
Lit les donnes des employs depuis une entre de donnes
@param in La saisie des donnes
*/
public void readData(DataInput in) throws IOException
{
name = DataIO.readFixedString(NAME_SIZE, in);
salary = in.readDouble();
int y = in.readInt();
int m = in.readInt();
int d = in.readInt();
GregorianCalendar calendar
= new GregorianCalendar(y, m - 1, d);
// GregorianCalendar utilise 0 = janvier
hireDay = calendar.getTime();
}
public static final int NAME_SIZE = 40;
public static final int RECORD_SIZE
= 2 * NAME_SIZE + 8 + 4 + 4 + 4;
private String name;
private double salary;
private Date hireDay;
}
class DataIO
{
public static String readFixedString(int size,
DataInput in) throws IOException
{
StringBuffer b = new StringBuffer(size);
int i = 0;
boolean more = true;
while (more && i < size)
{
char ch = in.readChar();
i++;
if (ch == 0) more = false;
else b.append(ch);
}
in.skipBytes(2 * (size - i));
return b.toString();
}
public static void writeFixedString(String s, int size,
DataOutput out) throws IOException
{
int i;
for (i = 0; i < size; i++)
{
char ch = 0;

741

Livre Java .book Page 742 Jeudi, 25. novembre 2004 3:04 15

742

Au cur de Java 2 - Notions fondamentales

if (i < s.length()) ch = s.charAt(i);


out.writeChar(ch);
}
}
}

Les flux dobjets


Lemploi denregistrements de longueur fixe convient trs bien des donnes de mme type. En
programmation oriente objet, les objets sont rarement de mme type. Considrons un tableau
appel staff form denregistrements demploys [Employee] dont certains sont des instances
dune sous-classe dEmployee, par exemple Manager.
Pour enregistrer dans des fichiers ce type dinformation, il faut commencer par enregistrer le type de
chaque objet, puis les donnes donnant ltat courant de lobjet. Inversement, la relecture du
fichier, il faudra :
m

lire le type de lobjet ;

crer un objet vide de ce type ;

y placer les donnes provenant du fichier.

Il est tout fait possible, mais trs fastidieux, de faire cela la main. Nous verrons bientt la puissance de ce mcanisme, appel srialisation des objets qui automatise presque compltement le
processus prcdent (le terme "srialisation" sera expliqu plus loin).

Ecrire des objets de types variables


Pour enregistrer les donnes, il faut au pralable ouvrir un objet ObjectOutputStream:
ObjectOutputStream out = new ObjectOutputStream(new
FileOutputStream("employee.dat"));

Puis pour enregistrer lobjet, on utilise la mthode writeObject de la classe ObjectOutputStream,


comme dans ce programme :
Employee harry = new Employee("Harry Hacker", 50000,
1989, 10, 1);
Manager boss = new Manager("Carl Cracker", 80000,
1987, 12, 15);
out.writeObject(harry);
out.writeObject(boss);

Pour relire les objets, on commence par instancier un objet ObjectInputStream:


ObjectInputStream in = new ObjectInputStream(new
FileInputStream("employee.dat"));

Puis on lit les objets dans lordre dans lequel ils ont t crits avec la mthode readObject:
Employee e1 = (Employee)in.readObject();
Employee e2 = (Employee)in.readObject();

Il faut, lorsque lon recharge des objets, respecter exactement le nombre dobjets enregistrs, leur
succession et leurs types. Chaque appel readObject lit un autre objet du type Object, quil faut
transtyper dans son type exact.

Livre Java .book Page 743 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

743

Si le vritable type de lobjet nest pas connu ou nest pas utile, il suffit de le transtyper dans le type
dune quelconque de ses superclasses, ou mme de le laisser dans le type Object. Dans lexemple
prcdent, e2 rfrence un objet Employee, mme sil rfrence en fait un objet Manager. Le type
dun objet peut tre obtenu dynamiquement avec la mthode getClass dcrite au Chapitre 5.
On ne peut crire et lire que des objets avec les mthodes writeObject/readObject. Pour des
valeurs de type primitif, on utilisera des mthodes comme writeInt/readInt ou writeDouble/
readDouble (les classes de flux dobjets implmentent les interfaces DataInput/DataOutput).
Bien videmment, les valeurs numriques se trouvant lintrieur des objets (le champ salaire dun
objet Employee par exemple) sont enregistres et recharges automatiquement. Les chanes et les
tableaux, qui sont en Java des objets, relvent par consquent des mthodes writeObject/readObject.
Il faut cependant modifier lgrement toute classe devant tre enregistre et recharge partir dun
flux dobjets : la classe doit implmenter linterface Serializable.
class Employee implements Serializable { . . .}

Comme linterface Serializable ne possde pas de mthode, il ny a absolument rien changer


dans vos classes. Sur ce point, elle ressemble linterface Cloneable galement vue au Chapitre 6,
bien que, pour pouvoir cloner une classe, il soit ncessaire de surcharger la mthode clone de la
classe Object. Pour rendre une classe srialisable, il ny a rien faire de plus.
LExemple 12.4 est un programme de test enregistrant un tableau contenant deux employs et un
cadre sur le disque, puis le relisant. Ecrire un tableau se fait en une opration :
Employee[] staff = new Employee[3];
. . .
out.writeObject(staff);

Par ailleurs, la lecture du flux se fait en une seule opration. Il est ncessaire de transtyper la valeur
renvoye par la mthode readObject:
Employee[] newStaff = (Employee[])in.readObject();

Linformation recharge, nous imprimons chaque employ, car on peut distinguer immdiatement
les objets employs de lobjet cadre grce leffet diffrent que va avoir toString. Cela nous prouvera
que nous avons restaur les objets dans leurs types corrects.
Exemple 12.4 : ObjectFileTest.java
import java.io.*;
import java.util.*;
class ObjectFileTest
{
public static void main(String[] args)
{
Manager boss = new Manager("Carl Cracker", 80000,
1987, 12, 15);
boss.setBonus(5000);
Employee[] staff = new Employee[3];
staff[0] = boss;
staff[1] = new Employee("Harry Hacker", 50000,
1989, 10, 1);

Livre Java .book Page 744 Jeudi, 25. novembre 2004 3:04 15

744

Au cur de Java 2 - Notions fondamentales

staff[2] = new Employee("Tony Tester", 40000,


1990, 3, 15);
try
{
// enregistrer tous les dossiers employ dans le
// fichier employee.dat
ObjectOutputStream out = new ObjectOutputStream(new
FileOutputStream("employee.dat"));
out.writeObject(staff);
out.close();
// rcuprer tous les enregistrements dans un nouveau tableau
ObjectInputStream in = new ObjectInputStream(new
FileInputStream("employee.dat"));
Employee[] newStaff = (Employee[])in.readObject();
in.close();
// afficher les enregistrements employ nouvellement lus
for (int i = 0; i < newStaff.length; i++)
System.out.println(newStaff[i]);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
class Employee implements Serializable
{
public Employee() {}
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
// GregorianCalendar utilise 0 = janvier
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}

Livre Java .book Page 745 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

public void raiseSalary(double byPercent)


{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+ "]";
}
private String name;
private double salary;
private Date hireDay;
}
class Manager extends Employee
{
/**
@param n Le nom de lemploy
@param s Le salaire
@param year Lanne dembauche
@param year Le mois de lembauche
@param year Le jour de lembauche
*/
public Manager(String n, double s,
int year, int month, int day)
{
super(n, s, year, month, day);
bonus = 0;
}
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public void setBonus(double b)
{
bonus = b;
}
public String toString()
{
return super.toString()
+ "[bonus=" + bonus
+ "]";
}
private double bonus;
}

Les flux et les fichiers

745

Livre Java .book Page 746 Jeudi, 25. novembre 2004 3:04 15

746

Au cur de Java 2 - Notions fondamentales

java.io.ObjectOutputStream 1.1

ObjectOutputStream(OutputStream out)

Cre un ObjectOutputStream pour permettre dcrire des objets dans le flux spcifi OutputStream.

void writeObject(Object obj)

Ecrit lobjet spcifi dans un flux ObjectOutputStream. Sont crites la classe de lobjet, la
signature de la classe, les valeurs de tous ses champs non marqus transient ainsi que les
champs non statiques de toutes ses superclasses.
java.io.ObjectInputStream 1.1

ObjectInputStream(InputStream is)

Cre un objet ObjectInputStream pour relire linformation sur lobjet dans le flux InputStream.

Object readObject()

Lit un objet dans le flux ObjectInputStream. Cette mthode relit, entre autres, la classe de
lobjet, la signature de la classe et les valeurs des champs non statiques, qui ne sont pas marques
transient ("non transient"), de la classe et de toutes ses superclasses. Elle effectue la dsrialisation pour permettre la rcupration des rfrences multiples un objet.

La srialisation des objets


La srialisation des objets sauvegarde les donnes des objets dans un format particulier. Il est
parfaitement possible dutiliser les mthodes writeObject/readObject sans rien connatre de
la squence exacte doctets reprsentant les objets dans le fichier. Lexamen du format des
donnes donne un aperu du processus permettant de placer des objets dans un flux ; nous le
montrons sur des vidages mmoire en hexadcimal de quelques objets aprs sauvegarde. Cette
partie est un peu technique et vous tes libre de la passer si limplmentation de la srialisation
ne vous intresse pas.
Tout fichier commence par un "nombre magique" sur 2 octets :
AC ED

suivi par le numro de version du format de srialisation des objets, soit actuellement :
0005

(Les octets sont nots en hexadcimal dans toute cette partie.)


Suit alors une squence dobjets dans lordre o ils ont t sauvegards.
Les objets de type string sont enregistrs ainsi :
74

la longueur sur 2 octets

les caractres de la chane

Par exemple, la chane "Harry" est enregistre ainsi :


74

00

05

H a r r y

Les caractres Unicode de la chane sont enregistrs au format UTF-8 modifi.

Livre Java .book Page 747 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

747

Quand un objet est enregistr, la classe de lobjet doit ltre galement. La description de la classe
contient :
m

le nom de la classe ;

une empreinte, calcule partir des champs de donnes et des signatures des mthodes, et constituant un numro didentification unique ;

un ensemble dindicateurs dcrivant la mthode de srialisation ;

la description des champs de donnes.

Java calcule lempreinte de la faon suivante :


m

Les descriptions de la classe, de ses superclasses, des interfaces, des types des champs et la
signature des mthodes sont places dans un ordre canonique.

Lalgorithme SHA (algorithme sr de hachage) est appliqu ces donnes.

SHA est un algorithme trs rapide permettant de fabriquer une empreinte dun important bloc de
donnes. Lempreinte a toujours 20 octets, quelle que soit la taille des donnes traites. Une suite
astucieuse doprations binaires permet dtre certain 100 % que, si les donnes sont modifies
dune quelconque faon, lempreinte le sera galement. Cependant, Java nutilise que les 8 premiers
octets du code SHA comme empreinte de la classe. Il reste fort probable que lempreinte de classe
changera si les champs de donnes ou les mthodes sont elles-mmes modifies.
Java se sert de lempreinte pour protger lutilisateur contre le scnario qui suit. Un objet a t enregistr sur disque. Le concepteur de la classe apporte une modification : par exemple, il supprime un
champ de donnes. La version sur disque est alors recharge. Lorganisation des donnes sur disque
ne correspond plus lorganisation en mmoire. Les donnes, si elles taient relues dans leur forme
ancienne, deviendraient incorrectes. Java empche cela en vrifiant, grce lempreinte, que la dfinition de la classe na pas chang au moment o lon recharge un objet : lempreinte sur disque doit
tre identique lempreinte de la classe utilise.
INFO
Sur le plan technique, tant que lorganisation dune classe ne change pas, on peut recharger des objets, instances de
cette classe. Cependant, Java, trs prudemment, vrifie que les mthodes nont pas non plus t modifies (en fait,
ce sont les mthodes qui fixent linterprtation des donnes). Dans la pratique, les classes ne restent pas immuables,
et nous pourrons avoir lire des versions anciennes dobjets (voir plus loin la gestion des versions).

Voici comment un identificateur de classe est stock :


m

72;

longueur du nom de la classe sur 2 octets ;

nom de la classe ;

empreinte sur 8 octets ;

indicateur sur 1 octet ;

nombre de descripteurs de champs sur 2 octets ;

descripteurs de champs de donnes ;

Livre Java .book Page 748 Jeudi, 25. novembre 2004 3:04 15

748

Au cur de Java 2 - Notions fondamentales

78 (marqueur de fin) ;

type de la superclasse (70 sil ny en a pas).

Loctet de lindicateur est obtenu par trois masques binaires, dfinis ainsi :
java.io.ObjectStreamConstants:
static final byte SC_WRITE_METHOD = 1;
// cette classe possde une mthode writeObject qui crit des donnes
supplmentaires
static final byte SC_SERIALIZABLE = 2;
// Cette classe implmente linterface Serializable
static final byte SC_EXTERNALIZABLE = 4;
// Cette classe implmente linterface Externalizable

Nous verrons plus loin dans ce chapitre linterface Externalizable. Les classes Externalizable
fournissent des mthodes de lecture/criture personnalises qui prennent en charge les sorties de
leurs champs instancis. Pour le moment, tous nos exemples de classes implmentent linterface
Serializable et leur indicateur vaut 02. La classe java.util.Date dfinit ses propres mthodes
readObject/writeObject et possde un drapeau 03.
Un descripteur de champ a le format :
m

codage du type sur un octet ;

longueur du nom du champ sur 2 octets ;

nom du champ ;

nom de la classe (si le champ est un objet).

Voici le codage des types possibles :


B

byte

char

double

float

int

long

object

short

Boolean

array

Quand le code du type est L, le nom du champ est suivi par le type du champ. Les chanes des noms
de classes et de champs ne commencent pas par le code 74, la diffrence des types de champs. Les
types de champs utilisent un encodage un peu diffrent de leurs noms, savoir le format des mthodes
natives.
Par exemple, le champ salaire de la classe Employ est cod ainsi :
D 0006 salary

Livre Java .book Page 749 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

749

Voici le descripteur complet de la classe Employee:


720008 Employee
E6 D2867D AE AC 181B 02
Empreinte et drapeaux
0003
Nombre de champs instancis
D 0006 salary
Nom et type du champ instanci
L 0007 hireday
Nom et type du champ instanci
740010 Ljava/util/Date
Nom de la classe du champ-string
L 0004 name
Nom et type du champ instanci
740012 Ljava/lang/String
Nom de la classe du champ-string
78
Marqueur de fin
70
Pas de superclasse
Ces descripteurs sont relativement longs. Lorsquun mme descripteur doit tre utilis nouveau
dans le fichier, une forme abrge lui est substitue :
71
numro dordre sur 4 octets
Le numro dordre est une rfrence au descripteur de classe prcdent et explicite. Nous verrons
plus loin la mthode de numrotation.

Un objet est stock ainsi :


73
Prenons par exemple un objet Employee:

descripteur de classesdonnes de lobjet

40 E86A 0000000000
valeur du champ salary double
73
valeur du champ hireDay nouvel objet
71007E 0008
classe existante java.util.Date
7708000000911B 4E B18078
stockage externe voir plus loin
74000C Harry Hacker
valeur du champ nom chane
Comme vous pouvez le voir, le fichier de donnes contient assez dinformation pour restaurer lobjet
Employee.

Les tableaux sont sauvegards dans le format suivant :


75
descripteur de classe
nombre dentres sur 4 octetsentres
Le nom de la classe tableau dans le descripteur de classe est dans le mme format que celui quutilisent les mthodes natives (il diffre un peu du nom de classe utilis par les noms de classes dans les
autres descripteurs de classe). Dans ce format, les noms de classes commencent par un L et finissent
par un point-virgule.

Voici comment se prsente un tableau de trois objets du type Employee.


75
72000B [LEmployee;
FC BF 3611 C59111 C702
0000
78
70
00000003

Tableau
Nouvelle classe, longueur de chane, nom de la classe
Empreinte et drapeaux
Nombre de champs instancis
Marqueur de fin
Pas de superclasse
Nombre dentres

Livre Java .book Page 750 Jeudi, 25. novembre 2004 3:04 15

750

Au cur de Java 2 - Notions fondamentales

Lempreinte pour un tableau dobjets du type Employee diffre de celle de la classe Employee ellemme.
Certes, ltude de ces codes est peu prs aussi passionnante que la lecture des pages jaunes de
lannuaire, mais il est bon de savoir quun flux dobjets contient une description dtaille des objets
qui sy trouvent, avec toute linformation ncessaire la reconstruction aussi bien dobjets que de
tableaux dobjets.

Rsoudre le problme de lcriture des rfrences dobjets


Nous savons enregistrer des objets contenant des nombres, des chanes ou dautres objets simples
(comme lobjet Day de la classe Employee). Il reste un cas important : que se passe-t-il quand un
objet est partag par plusieurs objets en tant que donne membre ?
Pour illustrer le problme, modifions lgrement la classe Manager. Supposons que chaque manager dispose dun secrtaire, implment comme une variable instancie du type Employee (on aurait
trs bien pu faire driver une classe Secretary de la classe Employee cet effet, mais nous ne le
ferons pas ici) :
class Manager extends Employee
{
. . .
private Employee secretary;
}

Cela fait, gardez lesprit que lobjet Manager contient dsormais une rfrence lobjet Employee
qui dcrit le secrtaire et non une copie spare de lobjet.
Deux cadres peuvent partager le mme secrtaire (voir Figure 12.6), do le code suivant :
harry = new Employee("Harry Hacker", . . .);
Manager carl = new Manager("Carl Cracker", . . .);
carl.setSecretary(harry);
Manager tony = new Manager("Tony Tester", . . .);
tony.setSecretary(harry);

Enregistrons maintenant les donnes des employs sur disque. Il faut absolument viter que Manager
sauvegarde ses donnes selon la logique :
m

enregistrer les donnes de lemploy ;

enregistrer les donnes du secrtaire.

Car, dans ce cas, les donnes correspondant harry seraient enregistres trois fois. Au rechargement,
les objets prendraient alors la configuration de la Figure 12.7.
Ce nest pas ce que nous voulons. Supposons que le secrtaire soit augment : nous nallons pas
faire la chasse toutes les copies de lobjet pour rpercuter laugmentation. Il faut bien videmment sauvegarder et recharger une copie unique du secrtaire. A cet effet, nous devons copier et
recharger les rfrences aux objets telles quelles se trouvent initialement : la disposition de
lobjet sur disque doit tre exactement celle de lobjet en mmoire. Dans le jargon de la POO, on
appelle cela la persistance.
Bien entendu, il ne sagit pas ici de sauvegarder et de restaurer les adresses en mmoire des objets
secrtaires. Quand un objet est recharg, il va a priori se trouver une adresse mmoire compltement
diffrente de ladresse initiale.

Livre Java .book Page 751 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

Employee

staff =

name = "Harry Hacker"

Manager

name = "Carl Cracker"


secretary =

Manager

name =

"Tony Tester"

secretary =

Employee

staff =

name = "Harry Hacker"

Manager
name = "Carl Cracker"
secretary =

Employee

name = "Harry Hacker"

Manager
name = "Tony Tester"
secretary =

Employee

name = "Harry Hacker"

751

Livre Java .book Page 752 Jeudi, 25. novembre 2004 3:04 15

752

Au cur de Java 2 - Notions fondamentales

Java utilise une approche dite de srialisation, do le nom du mcanisme, dit de srialisation des
objets. En voici lalgorithme :
m

Tous les objets sauvegards sur disque reoivent un numro dordre (1, 2, 3, etc., comme le
montre la Figure 12.8).

Quand un objet est enregistr sur disque, il faut rechercher si le mme objet a dj t sauvegard.

Si cest le cas, lobjet doit tre marqu comme "identique un objet prcdemment sauvegard et possdant le numro dordre x", sinon les donnes compltes de lobjet doivent tre
sauvegardes.
Memory

Employee

File

.
.
.
serial number = 1
type = Employee
name = "Harry Hacker"

name = "Harry Hacker"


serial number = 2
type = Manager
name = "Carl Cracker"
secretary = object 1

Manager
name = "Carl Cracker"

serial number = 3
type = Manager
name = "Tony Tester"
secretary = object 1

.
.
.

secretary =

Manager
name =

"Tony Tester"

secretary =

Pour recharger des objets, on excute la procdure inverse. Pour chaque objet charger, il faut noter
son numro de squence et lemplacement o il va tre charg en mmoire. Si lindicateur "identique
un objet prcdemment sauvegard et possdant le numro dordre x" est vrai, il est ncessaire de
savoir o lobjet numrot x a t charg et de forcer la rfrence de lobjet cette adresse mmoire.
Les objets peuvent tre enregistrs dans un ordre quelconque. La Figure 12.9 donne un exemple de
cadre plac en tte du tableau staff.
Tout ce qui prcde peut dconcerter, mais il sagit dun processus entirement automatique implment pour les flux dobjets, faisant lallocation des numros dordre et la gestion des objets dupliqus.

Livre Java .book Page 753 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

753

Nous allons voir maintenant lalgorithme exact de numrotation qui est un peu diffrent de celui
utilis pour les figures.
INFO
La srialisation sert, dans ce chapitre, enregistrer une collection dobjets sur disque et les recharger sans aucune
modification. Il existe une autre application, extrmement importante : la transmission dune collection dobjets vers
un autre ordinateur via une connexion sur un rseau. Les adresses mmoire nont pas plus de signification dans ce
cas que pour un fichier. La srialisation les remplaant par une numrotation des objets permet le transport de collections dobjets dune machine une autre, ventuellement dote dun processeur diffrent.

Memory

Manager
name = "Carl Cracker"

File

.
.
.
serial number = 1
type = Manager
name = "Carl Cracker"
secretary =
serial number = 2
type = Employee
name = "Harry Hacker"

secretary =

Employee

name = "Harry Hacker"

serial number = 3
type = Manager
name = "Tony Tester"
secretary = object 2

.
.
.

Manager
name =

"Tony Tester"

secretary =

Le programme suivant (voir Exemple 12.5) enregistre puis recharge un ensemble demploys et de
cadres interconnects. Certains partagent le mme employ en tant que secrtaire. On constatera que
lobjet secrtaire reste unique aprs rechargement : quand newStaff[1] augmente, il est possible de
le voir grce au champ secretary des cadres.
Exemple 12.5 : ObjectRefTest.java
import java.io.*;
import java.util.*;
class ObjectRefTest

Livre Java .book Page 754 Jeudi, 25. novembre 2004 3:04 15

754

Au cur de Java 2 - Notions fondamentales

{
public static void main(String[] args)
{
Employee harry = new Employee("Harry Hacker", 50000,
1989, 10, 1);
Manager boss = new Manager("Carl Cracker", 80000,
1987, 12, 15);
boss.setSecretary(harry);
Employee[] staff = new Employee[3];
staff[0]
staff[1]
staff[2]
1990,

= boss;
= harry;
= new Employee("Tony Tester", 40000,
3, 15);

try
{
// enregistrer tous les dossiers employ dans le
// fichier employee.dat
ObjectOutputStream out = new ObjectOutputStream(new
FileOutputStream("employee.dat"));
out.writeObject(staff);
out.close();
// rcuprer tous les enregistrements dans un nouveau tableau
ObjectInputStream in = new ObjectInputStream(new
FileInputStream("employee.dat"));
Employee[] newStaff = (Employee[])in.readObject();
in.close();
// augmenter le salaire du secrtaire
newStaff[1].raiseSalary(10);
// afficher les enregistrements employ nouvellement lus
for (int i = 0; i < newStaff.length; i++)
System.out.println(newStaff[i]);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
class Employee implements Serializable
{
public Employee() {}
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
// GregorianCalendar utilise 0 = janvier
hireDay = calendar.getTime();
}

Livre Java .book Page 755 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

public String getName()


{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+ "]";
}
private String name;
private double salary;
private Date hireDay;
}
class Manager extends Employee
{
/**
Construit un Manager sans secrtaire
@param n Le nom de lemploy
@param s Le salaire
@param year Lanne dembauche
@param year Le mois dembauche
@param year Le jour dembauche
*/
public Manager(String n, double s,
int year, int month, int day)
{
super(n, s, year, month, day);
secretary = null;
}
/**
Attribue un secrtaire au cadre.
@param s Le secrtaire
*/
public void setSecretary(Employee s)
{

Les flux et les fichiers

755

Livre Java .book Page 756 Jeudi, 25. novembre 2004 3:04 15

756

Au cur de Java 2 - Notions fondamentales

secretary = s;
}
public String toString()
{
return super.toString()
+ "[secretary=" + secretary
+ "]";
}
private Employee secretary;
}

Comprendre le format de sortie des rfrences dobjets


Poursuivons maintenant lanalyse du format de sortie des flux dobjets. Si vous avez saut lanalyse
prcdente, vous devriez en principe passer aussi celle-ci.
Tous les objets (y compris les tableaux et les chanes) et tous les descripteurs de classe sont numrots quand ils sont sauvegards dans le fichier de sortie. Ce processus se nomme srialisation du fait
de cette numrotation squentielle (qui commence 007E 0000.)
Nous avons vu quun descripteur de classe (non abrg) napparat quune seule fois. Les descripteurs suivants font rfrence ce premier descripteur. Ainsi, dans notre premier exemple, une autre
rfrence la classe Date tait code :
71007E 0008

Le mme mcanisme vaut pour les objets. Une rfrence un objet dj sauvegard a exactement le
mme aspect dans le fichier, cest--dire 71 suivi par le numro dordre. Le contexte permet toujours
de distinguer un descripteur de classe dun descripteur dobjet.
Enfin, la rfrence vide a cet aspect :
70

Commentons maintenant la sortie fichier du programme ObjectRefTest que nous venons de voir.
Vous pouvez trs bien lancer le programme, sortir le vidage en hexadcimal du fichier de donnes
employee.dat et le comparer notre listing comment. Les lignes importantes (en gras) se trouvant
vers la fin de la sortie font apparatre une rfrence un objet dj sauvegard.
En-tte de fichier
Tableau staff (N de srie #1)
Nouvelle classe, longueur de chane, nom de classe Employee
[](N de srie #0)
FC BF 3611 C59111 C702 Empreinte et drapeaux
0000
Nombre de champs instancis
78
Marqueur de fin
70
Pas de superclasse
00000003
Nombre dentres du tableau
73
staff[0] nouvel objet (N de srie #7)
720007 Manager
Nouvelle classe, longueur de chane, nom de classe (N de srie #2)
3606 AE 13638F 59 B702 Empreinte et drapeaux

AC ED 0005
75
72000B [LEmployee;

Livre Java .book Page 757 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

0001
L 0009 secretary
74000A LEmployee;
78
720008 Employee
E6 D2867D AE AC 181B 02
0003
D 0006 salary
L 00 07 hireDay
740010 Ljava/util/Date;
L 0004 name
740012 Ljava/lang/String;
78
70
40 F3880000000000
73
72000E java.util.Date
686A 81014B 59741903
0000
78
70
7708
00000083 E939 E000
78
74000C Carl Craker
73
71007E 0008
40 E86A 0000000000
73
71007E 0008
7708
000000911B 4E B180
78
74000C Harry Hacker
71007E 000B
73
71007E 0004
40 E3880000000000

Les flux et les fichiers

757

Nombre de champs de donnes


Type et nom du champ instanci
Nom de la classe du champ instanci String (N de srie #3)
Marqueur de fin
Superclasse nouvelle classe, longueur de chane, nom de classe
(N de srie #4)
Empreinte et drapeaux
Nombre de champs instancis
Type de champ instanci et nom
Type de champ instanci et nom
Nom de la classe du champ instanci String (N de srie #5)
Type de champ instanci et nom
Nom de la classe du champ instanci String (N de srie #6)
Marqueur de fin
Pas de superclasse
Valeur du champ salary double
Valeur du champ hireDay nouvel objet (N de srie #9)
Nouvelle classe, longueur de chane, nom de classe (N de srie #8)
Empreinte et drapeaux
Pas de variables dinstance
Marqueur de fin
Pas de superclasse
Stockage externe, nombre doctets
Date
Marqueur de fin
Valeur du champ name String (N de srie #10)
Valeur du champ secretary nouvel objet (N de srie #11)
Classe existante (utilise le N de srie #4)
valeur du champ salary double
valeur du champ hireDay nouvel objet (N de srie #12)
Classe existante (utilise le N de srie #8)
Stockage externe, nombre doctets
Date
Marqueur de fin
Valeur du champ name String (N de srie #13)
staff[1] objet existant (utilise le N de srie #11)
staff[2] nouvel objet (N de srie #14)
Classe existante (utilise le N de srie #4)
valeur du champ salary double

Livre Java .book Page 758 Jeudi, 25. novembre 2004 3:04 15

758

Au cur de Java 2 - Notions fondamentales

valeur du champ hireDay nouvel objet (N de srie #15)


71007E 0008
Classe existante (utilise le N de srie #8)
7708
Stockage externe, nombre doctets
000000946D 3E EC 0000 Date
78
Marqueur de fin
74000B Tony Tester
valeur du champ name String (N de srie #16)
Le format exact du fichier est sans importance (sauf si vous souhaitez modifier les donnes avec des
intentions malfaisantes). En rsum :
73

Un flux dobjets en sortie contient les types et les champs de donnes de tous les objets.

Chaque objet reoit un numro dordre.

Les rfrences multiples un mme objet sont sauvegardes comme les rfrences ce numro
dordre.

Modifier le mcanisme de srialisation par dfaut


Certains champs de donnes ne devraient jamais tre srialiss, par exemple les valeurs dentier qui
stockent des handles de fichiers ou des handles de fentres uniquement signifiants pour les mthodes
natives. Ces informations seront forcment inutiles lorsque vous rechargerez un objet ou que vous
les transporterez vers une autre machine. Les valeurs incorrectes pour ces champs peuvent en fait
faire planter les mthodes natives. Java possde un mcanisme simple pour empcher la srialisation
de ces champs. Marquez-les avec le mot cl transient. Vous devez aussi baliser les champs de la
mme manire sils appartiennent ces classes non srialisables. Les champs transitoires sont
toujours ignors lors de la srialisation des objets.
Ce mcanisme offre la possibilit de remplacer dans une classe son comportement par dfaut par une
validation ou par toute autre action ncessaire. Une classe srialisable peut dfinir des mthodes
avec la signature :
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void writeObject(ObjectOutputStream out)
throws IOException;

Les champs de donnes ne sont plus dsormais srialiss automatiquement ; ces mthodes sont
dabord appeles.
Voici un exemple typique. Plusieurs classes du package java.awt.geom, comme Point2D.Double,
ne sont pas srialisables. Supposons maintenant que vous vouliez srialiser une classe LabeledPoint qui stocke une chane et un Point2D.Double. Vous devez dabord marquer le champ
Point2D.Double comme transitoire pour viter une exception NotSerializableException:
public class LabeledPoint implements Serializable
{
. . .
private String label;
private transient Point2D.Double point;
}

Dans la mthode writeObject, nous crivons dabord le descripteur dobjet et le champ String,
ltat, en appelant la mthode defaultWriteObject. Il sagit dune mthode spciale de la classe

Livre Java .book Page 759 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

759

ObjectOutputStream qui ne peut tre appele que depuis une mthode writeObject dune classe
srialisable. Nous crivons ensuite les coordonnes du point, laide des appels standard DataOutput:
private void writeObject(ObjectOutputStream out)
throws IOException
{
out.defaultWriteObject();
out.writeDouble(point.getX());
out.writeDouble(point.getY());
}

Dans la mthode readObject, nous inversons le processus :


private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
in.defaultReadObject();
double x = in.readDouble();
double y = in.readDouble();
point = new Point2D.Double(x, y);
}

Un autre exemple est la classe java.util.Date, qui fournit ses propres mthodes writeObject et
readObject, lesquelles crivent la date en millisecondes depuis le 1er janvier 1970. La classe Date
possde une reprsentation interne complexe qui stocke la fois lobjet Calendar et un compte en
millisecondes, afin doptimiser les recherches. Ltat de Calendar est redondant et na pas besoin
dtre sauvegard.
Les mthodes readObject et writeObject nont se proccuper de sauvegarder et de recharger
que leurs champs de donnes. Elles devraient en principe ignorer les donnes de la superclasse ou
celles dautres classes.
Une classe peut dfinir son propre mcanisme de srialisation en implmentant linterface Externalizable. Il est alors ncessaire de dfinir deux mthodes :
public void readExternal(ObjectInputStream in)
throws IOException, ClassNotFoundException;
public void writeExternal(ObjectOutputStream out)
throws IOException;

A la diffrence des mthodes readObject et writeObject que nous venons de commenter, ces
mthodes vont tre entirement responsables de la sauvegarde et de la restauration de lensemble de
lobjet, y compris des donnes de la superclasse. Le mcanisme de srialisation ne fera quenregistrer la classe de lobjet dans le flux. Quand il charge un objet externalisable, le flux dobjets cre un
objet par lintermdiaire de son constructeur par dfaut, puis appelle la mthode readExternal.
Voici comment implmenter ces mthodes pour la classe Employee:
public void readExternal(ObjectInput s)
throws IOException
{
name = s.readUTF();
salary = s.readDouble();
hireDay = new Date(s.readLong());
}
public void writeExternal(ObjectOutput s)

Livre Java .book Page 760 Jeudi, 25. novembre 2004 3:04 15

760

Au cur de Java 2 - Notions fondamentales

throws IOException
{
s.writeUTF(name);
s.writeDouble(salary);
s.writeLong(hireDay.getTime());
}

ASTUCE
La srialisation est lente parce que la machine virtuelle doit dcouvrir la structure de chaque objet. Si vous tes trs
sensible la performance, et si vous lisez et crivez un grand nombre dobjets dune classe particulire, tudiez les
avantages dune interface Externalizable. Le site http://developer.java.sun.com/developer/TechTips/txtarchive/
Apr00_Stu.txt montre que, dans le cas dune classe employee, lutilisation doprations externes de lecture-criture
se rvle de 35 40 % plus rapide que la srialisation par dfaut.

ATTENTION
Les mthodes readObject et writeObject sont prives et ne peuvent tre appeles que par le mcanisme de
srialisation, alors que les mthodes readExternal et writeExternal sont publiques. En particulier, readExternal peut potentiellement permettre de modifier ltat dun objet existant.

INFO
Pour obtenir des variations plus exotiques de la srialisation, voir http://www.absolutejava.com/ serialization.

Srialisation des singletons et numrations sres


Vous devez apporter une attention particulire lorsque vous srialisez et dsrialisez les objets qui
sont supposs tre uniques. Ceci survient gnralement lorsque vous implmentez des singletons et
des numrations sres.
Si vous utilisez la construction enum du JDK 5.0, ne vous inquitez pas de la srialisation (elle fonctionne, tout simplement). Supposons toutefois que vous conserviez un code qui contienne un type
numr comme
public class Orientation
{
public static final Orientation HORIZONTAL = new Orientation(1);
public static final Orientation VERTICAL = new Orientation(2);
private Orientation(int v) { value = v; }
private int value;
}

Cette construction tait commune avant lapparition des numrations dans le langage Java.
Sachez que le constructeur est priv. Ainsi, aucun objet ne peut tre cr au-del de Orientation.HORIZONTAL et Orientation.VERTICAL. Vous pouvez en particulier utiliser loprateur ==
pour tester lgalit de lobjet :
if (orientation == Orientation.HORIZONTAL) . . .

Livre Java .book Page 761 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

761

Un changement important a eu lieu, dont vous devez vous souvenir lorsquune numration sre
implmente linterface Serializable. Le mcanisme de srialisation par dfaut ne convient pas.
Supposons que nous crivions une valeur du type Orientation et que nous la lisions nouveau :
Orientation original = Orientation.HORIZONTAL;
ObjectOutputStream out = . . .;
out.write(value);
out.close();
ObjectInputStream in = . . .;
Orientation saved = (Orientation) in.read();

Le test
if (saved == Orientation.HORIZONTAL) . . .

chouera ensuite. En fait, la valeur enregistre est un objet totalement nouveau du type Orientation
et qui nest gal aucune des constantes prdfinies. Mme si le constructeur est priv, le mcanisme de srialisation peut crer de nouveaux objets !
Pour rsoudre ce problme, vous devez dfinir une autre mthode de serialisation spciale, appele
readResolve. Lorsque cette mthode est dfinie, elle est appele aprs que lobjet eut t dsrialis. Elle doit renvoyer un objet qui deviendra ensuite la valeur de retour de la mthode readObject.
Dans notre cas, la mthode readResolve inspectera le champ de valeur et renverra la constante
numre approprie :
protected Object readResolve() throws ObjectStreamException
{
if (value == 1) return Orientation.HORIZONTAL;
if (value == 2) return Orientation.VERTICAL;
return null; // ceci ne devrait pas arriver
}

Noubliez pas dajouter une mthode readResolve toutes les numrations sres de votre code et
toutes les classes qui suivent le motif de conception du singleton.

La gestion des versions


Dans ce qui prcde, nous avons pris, titre de dmonstration, des collections dobjets relativement
petites pour les placer dans un flux. Les flux dobjets permettent doprer grande chelle. Imaginons un programme du genre diteur de texte. Le document produit contiendra des paragraphes de
texte, des tableaux, des graphiques, etc. Lensemble du document peut tre envoy dans un flux par
un simple appel writeObject:
out.writeObject(doc);

Les objets paragraphe, tableau et graphique sont tous automatiquement placs dans le flux. Toute
personne disposant de votre programme peut charger une copie du fichier par un simple appel
readObject:
doc = (Document)in.readObject();

Cest trs utile de pouvoir ainsi transmettre des donnes, mais invitablement votre programme va
voluer et vous produirez une version 1.1. Les anciens fichiers seront-ils encore lisibles ? Les utilisateurs de la version 1.0 pourront-ils lire les fichiers produits par la nouvelle version ? Il est bien
videmment souhaitable que les fichiers dobjets acceptent lvolution des classes.

Livre Java .book Page 762 Jeudi, 25. novembre 2004 3:04 15

762

Au cur de Java 2 - Notions fondamentales

Cela semble impossible premire vue. Quand une dfinition de classe change, son empreinte SHA
change galement et, comme nous lavons vu, les flux dobjets refusent de lire des objets ayant des
empreintes diffrentes. Rien nempche cependant dindiquer quune classe est compatible avec une
version prcdente. Il faut commencer par obtenir lempreinte de la version prcdente de la classe :
le programme autonome serialver, fourni avec le JDK, va nous donner cette valeur numrique. Par
exemple, lexcution de
serialver Employee

affiche
Employee: static final long serialVersionUID =
-1814239825517340645L;

Si on lance serialver avec loption -show, le programme installe une bote de dialogue graphique
(voir Figure 12.10).
Figure 12.10
La version graphique du
programme serialver.

Toutes les versions ultrieures de la classe doivent dfinir la constante serialVersionUID avec la
valeur de lempreinte originale :
class Employee implements Serializable // version1.1
{
. . .
public static final long serialVersionUID
= -1814239825517340645L;
}

Quand une classe possde une donne membre statique appele serialVersionUID, elle ne calcule
pas elle-mme dempreinte, mais utilise cette valeur.
Le mcanisme de srialisation va maintenant, en trouvant cette donne membre dans la classe,
accepter de charger des versions diffrentes des objets de cette classe.
Si seules les mthodes ont chang, il ny a pas de problme pour lire les donnes du nouvel objet. En
revanche, si les donnes membres ont t modifies, on peut rencontrer des problmes. Par exemple,
lancien objet se trouvant dans le fichier peut possder plus ou moins de champs de donnes que
celui se trouvant dans le programme, ou les types des champs peuvent tre diffrents. Le flux
dobjets va tenter dans ce cas de convertir lobjet dans le flux la version courante de la classe.
Le flux dobjets compare les champs de donnes de la version courante de la classe avec les champs
courants de la version se trouvant dans le flux. Bien entendu, il ne prend en compte que les champs de
donnes non transients et non statiques. Si deux champs ont des noms identiques, mais sont de types
diffrents, le flux dobjets ne tente pas de convertir un type dans lautre : les objets sont incompatibles. Si lobjet se trouvant dans le flux possde des champs de donnes qui ne sont pas dans la
version courante, les donnes en excdent sont ignores. Si la version courante a des champs de
donnes qui ne se trouvent pas dans le flux, les champs nouveaux sont forcs leurs valeurs par
dfaut (null pour les objets, 0 pour les nombres et false pour les valeurs boolennes).

Livre Java .book Page 763 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

763

Voici un exemple. Supposons quun certain nombre denregistrements demploys soient sauvegards sur disque avec la version originale (1.0) de la classe. Passons une version 2.0 de la classe
Employee en ajoutant un champ de donnes appel department. La Figure 12.11 montre ce qui se
passe quand un objet 1.0 est charg par un programme utilisant les objets 2.0. Le champ department
est forc null. La Figure 12.12 correspond au scnario oppos : un programme utilisant les objets
1.0 lit un objet 2.0. Le champ ajout department est ignor.
Figure 12.11
Rechargement
dun objet
nayant pas tous
ses champs
de donnes.

File

serialized
version 1.0

Memory
version 2.0

.
.
.

Employee
name = "Harry Hacker"

serial number = . . .
type = Employee
name = "Harry Hacker"
salary = 35000.0
hireDay = 1989-01-15

salary =
hireDay =

.
.
.

Figure 12.12
Rechargement dun
objet ayant trop de
champs de donnes.

serialized
version 2.0

35000.0
1989-01-15

department =

File

null

Memory
version 1.0

.
.
.
serial number = . . .
type = Employee
name = "Harry Hacker"
salary = 35000.0
hireDay = 1989-01-15
department = "Finance"

.
.
.

Employee
name = "Harry Hacker"
salary =
hireDay =

35000.0
1989-01-15

Est-ce un processus sr ? Cela dpend des cas. Ignorer un champ parat sans inconvnient : le
programme a accs toutes les donnes quil est en mesure de manipuler. En revanche, forcer un
champ de donnes null peut prsenter des dangers. Dans beaucoup de classes, on sest efforc
dinitialiser systmatiquement toutes les donnes membres dans tous les constructeurs avec dautres
valeurs que null pour pargner aux mthodes davoir traiter une donne null. Cest au concepteur
de la classe dajouter du code dans la mthode readObject pour tenir compte des incompatibilits
des versions ou bien de sassurer que les mthodes ont une robustesse suffisante pour traiter des
valeurs null.

La srialisation comme outil de clonage


Il existe une application amusante (et parfois fort utile) de ce nouveau mcanisme de srialisation :
permettre le clonage dun objet avec une grande facilit condition que la classe soit srialisable (on
a vu au Chapitre 6 quil nest pas simple de cloner un objet).

Livre Java .book Page 764 Jeudi, 25. novembre 2004 3:04 15

764

Au cur de Java 2 - Notions fondamentales

Pour cloner un objet Serializable, il suffit de le srialiser vers un flux de sortie, puis de le relire.
Le rsultat est un nouvel objet qui est une copie de loriginal. Inutile de lcrire vers un fichier.
Il vous suffit dutiliser un ByteArrayOutputStream pour sauvegarder les donnes dans un tableau
doctets.
Comme le montre lExemple 12.6, pour obtenir librement clone tendez simplement la classe
SerialCloneable.
Vous devez comprendre que cette mthode, bien quastucieuse, sera en gnral beaucoup plus lente
quune mthode de clonage qui construit de faon explicite un nouvel objet et copie ou clone les
champs de donnes (comme nous lavons vu au Chapitre 6).
Exemple 12.6 : SerialCloneTest.java
import java.io.*;
import java.util.*;
public class SerialCloneTest
{
public static void main(String[] args)
{
Employee harry = new Employee("Harry Hacker", 35000,
1989, 10, 1);
// cloner Harry
Employee harry2 = (Employee)harry.clone();
// muter Harry
harry.raiseSalary(10);
// maintenant Harry et le clone sont diffrents
System.out.println(harry);
System.out.println(harry2);
}
}
/**
Une classe dont la mthode clone utilise la srialisation.
*/
class SerialCloneable implements Cloneable, Serializable
{
public Object clone()
{
try
{
// enregistrer lobjet dans un tableau doctets
ByteArrayOutputStream bout = new
ByteArrayOutputStream();
ObjectOutputStream out
= new ObjectOutputStream(bout);
out.writeObject(this);
out.close();
// lire un clone de lobjet dans le tableau doctets
ByteArrayInputStream bin = new
ByteArrayInputStream(bout.toByteArray());

Livre Java .book Page 765 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

ObjectInputStream in = new ObjectInputStream(bin);


Object ret = in.readObject();
in.close();
return ret;
}
catch (Exception e)
{
return null;
}
}
}
/**
La classe connue Employee, redfinie pour tendre
la classe SerialCloneable.
*/
class Employee extends SerialCloneable
{
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
// GregorianCalendar utilise 0 = janvier
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary

765

Livre Java .book Page 766 Jeudi, 25. novembre 2004 3:04 15

766

Au cur de Java 2 - Notions fondamentales

+ ",hireDay=" + hireDay
+ "]";
}
private String name;
private double salary;
private Date hireDay;
}

La gestion des fichiers


Nous avons vu comment lire et crire des donnes dans un fichier. La gestion des fichiers ne se rduit
pas ceci : la classe File encapsule les fonctionnalits ncessaires pour travailler avec le systme de
fichiers de la machine de lutilisateur. Par exemple, vous pouvez utiliser la classe File pour dterminer quand un fichier a t modifi pour la dernire fois ou pour supprimer ou renommer un fichier.
En un mot, les classes de flux travaillent sur le contenu des fichiers alors que la classe File opre sur
le stockage du fichier sur le disque.
NOTE
Comme cest souvent le cas en Java, la classe File ne traite que le dnominateur commun aux diverses plates-formes. Par
exemple, sous Windows, il est possible de lire et de positionner lindicateur de lecture seule dun fichier. En revanche, alors
quil est possible de dterminer quil sagit dun fichier cach, on ne peut le cacher sans recourir une mthode native.

Le constructeur le plus simple pour un objet File prend un nom de fichier complet. En labsence de
chemin, Java utilise le rpertoire courant. Par exemple,
File f = new File("test.txt");

donne accs un objet fichier de ce nom dans le rpertoire courant, cest--dire le rpertoire o
sexcute le processus qui excute la machine virtuelle. Si vous avez lanc la machine virtuelle
partir de la ligne de commande, cest le rpertoire partir duquel vous avez lanc lexcutable Java.
Un appel ce constructeur ne cre pas un fichier de ce nom sil nexiste pas. En fait, crer un fichier
quand on possde un objet File se fait par lun des constructeurs des classes flux ou par la mthode
createNewFile de la classe File. La mthode createNewFile ne va crer de fichier que sil
nexiste pas de fichier de ce nom, et renvoie un boolean pour indiquer si cela sest ralis.
Par ailleurs, si lon possde un objet File, la mthode exists de la classe File indique sil existe un
fichier de ce nom. Par exemple, le programme de test suivant va presque certainement afficher false,
quelle que soit la machine. De plus, il sera en mesure dafficher le chemin de ce fichier imaginaire :
import java.io.*;
public class Test
{
public static void main(String args[])
{
File f = new File("afilethatprobablydoesntexist");
System.out.println(f.getAbsolutePath());
System.out.println(f.exists());
}
}

Livre Java .book Page 767 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

767

Il existe deux autres constructeurs pour les objets File:


File(String path, String name)

cre un objet File avec le nom spcifi dans le rpertoire correspondant au path spcifi (ou, si
le paramtre path est null, dans le rpertoire courant) et en utilisant un objet File dans le
constructeur
File(File dir, String name)

o lobjet File reprsente un rpertoire. Comme prcdemment, si dir est null, le constructeur
cre un objet File dans le rpertoire courant.
De manire un peu dsordonne, un objet File peut reprsenter soit un fichier, soit un rpertoire
(peut-tre parce quil se trouve que le systme dexploitation le mieux connu par les concepteurs de
Java implmente les rpertoires comme des fichiers). Les mthodes isDirectory et isFile permettent de savoir sil sagit dun rpertoire ou dun fichier. Cest assez choquant dans un contexte
orient objet on sattend une classe distincte Directory, qui serait peut-tre une extension de la
classe File.
Pour instancier un objet reprsentant un rpertoire, il suffit de donner le nom dun rpertoire au
constructeur File:
File tempDir = new File(File.separator-"temp");

Si le rpertoire nexiste pas, la mthode mkdir va le crer :


tempDir.mkdir();

Si un objet file reprsente un rpertoire, on peut utiliser list() pour construire un tableau de noms
de fichiers de ce rpertoire. LExemple 12.7 utilise toutes ces mthodes pour afficher les sous-rpertoires dun rpertoire dont on entre le nom dans la ligne de commande (il nest pas difficile de transformer ce programme en une classe utilitaire renvoyant un vecteur des sous-rpertoires en vue dun
traitement quelconque).
ASTUCE
Utilisez toujours les objets File, et non des chanes, lorsque vous manipulez les noms de fichiers ou de rpertoires.
Par exemple, la mthode equals de la classe File sait que certains systmes de fichiers ne prennent pas garde la
casse et que placer un signe / dans un nom de rpertoire na pas dimportance.

Exemple 12.7 : FindDirectories.java


import java.io.*;
public class FindDirectories
{
public static void main(String[] args)
{
// si aucun argument nest fourni, commencer au rpertoire parent
if (args.length == 0) args = new String[] { ".." };
try
{
File pathName = new File(args[0]);
String[] fileNames = pathName.list();

Livre Java .book Page 768 Jeudi, 25. novembre 2004 3:04 15

768

Au cur de Java 2 - Notions fondamentales

// numrer tous les fichiers du rpertoire


for (int i = 0; i < fileNames.length; i++)
{
File f = new File(pathName.getPath(),
fileNames[i]);
// si le fichier est nouveau un rpertoire, appeler
// la mthode main de manire rcurrente
if (f.isDirectory())
{
System.out.println(f.getCanonicalPath());
main(new String [] { f.getPath() });
}
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
}

Plutt que de recenser tous les fichiers se trouvant dans un rpertoire, on peut passer un objet FileNameFilter en paramtre la mthode list pour raccourcir la liste. Ces objets sont simplement des
instances dune classe qui satisfait linterface FileNameFilter.
Pour implmenter linterface FilenameFilter, il suffit une classe de dfinir une mthode appele
accept(). Voici un exemple dune classe FilenameFilter trs simple qui ne laisse passer que les
fichiers possdant une certaine extension :
public class ExtensionFilter implements FilenameFilter
{
public ExtensionFilter(String ext)
{
extension = "."+ext;
}
public boolean accept(File dir, String name)
{
return name.endsWith(extension);
}
private String extension;
}

Si lon doit crire des programmes portables, cest un vrai dfi que de spcifier des noms de fichiers
avec des sous-rpertoires. Le slash (le sparateur pour UNIX) peut aussi tre utilis pour Windows,
mais dautres systmes dexploitation ne lautoriseront peut-tre pas, aussi dconseillons-nous son
usage.
ATTENTION
Si vous utilisez le slash comme sparateur de rpertoires sous Windows quand vous construisez un objet File, la
mthode getAbsolutePath renvoie un nom de fichier contenant des slashs qui vont surprendre les utilisateurs de
Windows. Il vaut mieux utiliser la mthode getCanonicalPath qui substitue des antislash aux slash.

Livre Java .book Page 769 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

769

Il est prfrable dutiliser linformation sur le sparateur courant pour les rpertoires, qui se trouve
dans un champ statique de la classe File appel separator. Dans lenvironnement Windows, il
sagit dun antislash (\) ; dans lenvironnement UNIX, il sagit dun slash (/). Par exemple :
File foo = new File("Documents"-File.separator-"data.txt")

Lautre constructeur de File donnera bien sr aussi le sparateur correct :


File foo = new File("Documents", "data.txt")

Nous avons gard de lAPI ce que nous croyons tre les mthodes les plus importantes de la classe
File; leur utilisation doit tre maintenant sans problme.
java.io.File 1.0

boolean canRead()

Prcise si le fichier peut tre lu par lapplication courante.

boolean canWrite()

Prcise si le fichier est en lecture seule ou en lecture/criture.

static boolean createTempFile(String prefix, String suffix) 1.2


stAtic boolean createTempFile(String prefix, String suffix, File directory) 1.2

Cre un fichier temporaire dans le rpertoire temporaire par dfaut du systme ou dans le rpertoire spcifi et forme le nom du fichier temporaire avec les prfixe et suffixe spcifis.
Paramtres :

prefix

Chane prfixe dau moins trois caractres.

suffix

Suffixe facultatif. Sil est null, .tmp est utilis.

directory

Rpertoire dans lequel le fichier est cr. Si null, le fichier


est cr dans le rpertoire courant.

boolean delete()

Tente de supprimer le fichier. Renvoie true si le fichier a t supprim, sinon false.

void deleteOnExit()

Demande la suppression du fichier au moment o la mmoire virtuelle est close.

boolean exists()

Renvoie true si le fichier ou le rpertoire existe, sinon false.

String getAbsolutePath()

Renvoie une chane contenant le chemin absolu. Astuce : utiliser plutt getCanonicalPath.

File getCanonicalFile() 1.2

Renvoie un objet File contenant le chemin du fichier dans un format canonique. Ainsi les rpertoires "." redondants sont limins, le sparateur de rpertoires correct est utilis, et lutilisation
des majuscules/minuscules se conforme aux prfrences du systme de fichiers.

String getCanonicalPath()

Renvoie une chane contenant le chemin du fichier dans un format canonique. Ainsi les rpertoires "." redondants sont limins, le sparateur de rpertoires correct est utilis, et lutilisation
des majuscules/minuscules se conforme aux prfrences du systme de fichiers.

String getName()

Renvoie une chane contenant le nom du fichier de lobjet File (sans mention du chemin).

Livre Java .book Page 770 Jeudi, 25. novembre 2004 3:04 15

770

Au cur de Java 2 - Notions fondamentales

String getParent()

Renvoie une chane contenant le rpertoire parent de cet objet File. Si cet objet File est un
fichier, alors le parent est le rpertoire qui le contient. Sil sagit dun rpertoire, alors le parent
est le rpertoire parent ou null si vous tes la racine.

File getParentFile() 1.2

Renvoie un objet File pour le parent de ce rpertoire. Voir getParent pour une dfinition du
"parent".

String getPath()

Renvoie une chane contenant le chemin du fichier.

boolean isDirectory()

Renvoie true si lobjet File reprsente un rpertoire, sinon false.

boolean isFile()

Renvoie true si lobjet File reprsente un fichier et non un rpertoire ou un priphrique.

boolean isHidden() 1.2

Renvoie true si lobjet File reprsente un fichier cach ou un rpertoire.

long lastModified()

Renvoie linstant de la dernire modification du fichier (compt en millisecondes depuis le


1er janvier 1970 GMT) ou 0 si le fichier nexiste pas. Utilisez le constructeur Date(long) pour
convertir cette valeur en date.

long length()

Renvoie la longueur du fichier en octets ou 0 si le fichier nexiste pas.

String[] list()

Renvoie un tableau de chanes contenant les noms des fichiers et rpertoires se trouvant dans
lobjet File ou null si lobjet File ne reprsente pas un rpertoire.

String[] list(FilenameFilter filter)

Renvoie un tableau contenant les noms des fichiers et rpertoires se trouvant dans lobjet File
qui satisfont au filtre ou null sil ny en a aucun.
Paramtres :

filter

Objet FilenameFilter utiliser comme filtre des noms de


fichiers.

File[] listFiles() 1.2

Renvoie un tableau dobjets File correspondant aux fichiers et rpertoires contenus dans cet
objet File ou null sil ny en a pas.

File[] listFiles(FilenameFilter filter) 1.2

Renvoie un tableau dobjets File correspondant aux fichiers et rpertoires contenus dans cet
objet File qui satisfont au filtre ou null sil ny en a pas.
Paramtres :

filter

Objet FilenameFilter utiliser comme filtre des noms de


fichiers.

File[] listRoots() 1.2

Renvoie un tableau dobjets File correspondant tous les fichiers racine existants. Par exemple,
sur un systme Windows, vous obtenez les objets File reprsentant les units de disques installes
(aussi bien locales que rseau). Sur un systme UNIX, vous obtenez simplement "/".

Livre Java .book Page 771 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

771

boolean createNewFile() 1.2

Cre de faon automatique un rpertoire dont le nom est donn par lobjet File, si aucun objet
correspondant nexiste dj. Cela signifie que la vrification du nom de fichier et la cration ne
sont pas interrompues par les autres activits ventuelles du systme de fichiers. Renvoie true si
le rpertoire a t cr avec succs, sinon false.

boolean mkdir()

Cre un rpertoire dont le nom est donn par lobjet File. Renvoie true si le rpertoire a t
cr avec succs, sinon false.

boolean mkdirs()

A la diffrence de mkdir, il cre les rpertoires parents si ncessaire. Renvoie false si lun au
moins des rpertoires requis na pu tre cr.

boolean renameTo(File dest)

Renvoie true si le nom a t modifi, sinon false.


Paramtres :

dest

Objet File spcifiant le nouveau nom.

boolean setLastModified(long time) 1.2

Modifie linstant de la dernire modification du fichier. Renvoie true si cest fait, sinon false.
Paramtres :

long

Entier long reprsentant le nombre de millisecondes depuis


lepoch ( minuit le 1/1/1970).

boolean setReadOnly() 1.2

Place le fichier en lecture seule. Renvoie true si cest fait, sinon false.

URL toURL() 1.2

Convertit lobjet File en un fichier dURL.


java.io.FilenameFilter

boolean accept(File dir, String name)

devrait tre dfini de faon renvoyer true si le fichier correspond au critre du filtre.
Paramtres :

dir

Objet File reprsentant le rpertoire contenant le fichier.

naxme

Nom du fichier.

Nouvelles E/S
Le JDK 1.4 contient plusieurs fonctionnalits destines amliorer le traitement des entres/
sorties, collectivement appeles "new I/O" ou "nouvelles E/S" dans le package java.nio (bien
entendu, le qualificatif "nouveau" est assez regrettable car sa nouveaut disparatra dans quelques
annes).
Le package prend en charge les fonctionnalits suivantes :
m

fichiers concordance de mmoire ;

verrouillage des fichiers ;

Livre Java .book Page 772 Jeudi, 25. novembre 2004 3:04 15

772

Au cur de Java 2 - Notions fondamentales

encodeurs et dcodeurs de jeux de caractres ;

non-blocage des E/S.

Nous avons dj introduit les jeux de caractres dans ce chapitre. Nous ne verrons ici que les deux
premires fonctionnalits. Le non-blocage des E/S exige lutilisation des threads, traits au Volume 2.

Fichiers concordance de mmoire


La plupart des systmes dexploitation peuvent tirer parti de limplmentation de la mmoire
virtuelle pour "faire concorder" un fichier ou une partie dun fichier avec la mmoire. Le fichier est
ensuite accessible comme sil sagissait dun tableau en mmoire, ce qui est plus rapide que les
oprations de fichiers traditionnelles.
A la fin de cette section, vous trouverez un programme qui calcule la somme de contrle CRC32 dun
fichier, laide dune entre de fichier traditionnelle et dun fichier concordance de mmoire. Sur une
machine, nous avons rcupr les donnes de timing prsentes au Tableau 12.7 lors du calcul de la
somme de contrle du fichier rt.jar, de 37 Mo, dans le rpertoire jre/lib du JDK.
Tableau 12.7 : Donnes de timing pour les oprations de fichier

Mthode

Dlai

Flux dentre brut

110 secondes

Flux dentre en tampon

9,9 secondes

Fichier daccs alatoire

162 secondes

Fichier concordance de mmoire

7,2 secondes

Comme vous pouvez le voir, sur cette machine particulire la concordance de mmoire est un peu
plus rapide que lutilisation dune entre squentielle en tampon et considrablement plus rapide que
lutilisation dun RandomAccessFile.
Bien entendu, les valeurs exactes diffreront considrablement dune machine lautre, mais il est
vident que les gains de performances peuvent tre substantiels si vous avez besoin dutiliser laccs
alatoire. Quant la lecture squentielle des fichiers de taille modre, il nexiste aucune raison
dutiliser la concordance de mmoire.
Le package java.nio simplifie considrablement la concordance de mmoire. Voici ce que vous
devez faire.
Rcuprez dabord un canal partir du fichier. Un canal est une abstraction pour les fichiers de
disque qui permet daccder aux fonctionnalits du systme dexploitation, par exemple la concordance de mmoire, le verrouillage des fichiers et des transferts de donnes rapides entre les fichiers.
Pour rcuprer un canal, appelez la mthode getChannel ajoute aux classes FileInputStream,
FileOutputStream et RandomAccessFile:
FileInputStream in = new FileInputStream(. . .);
FileChannel channel = in.getChannel();

Livre Java .book Page 773 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

773

Vous obtenez ensuite un MappedByteBuffer du canal en appelant la mthode map de la classe FileChannel. Vous spcifiez la zone du fichier faire concorder et un mode de concordance. Trois
modes sont pris en charge :
m

FileChannel.MapMode.READ_ONLY. Le tampon de rsultat est en lecture seule. Toute tentative


dcriture dans le tampon entrane une exception ReadOnlyBufferException.

FileChannel.MapMode.READ_WRITE. Le tampon de rsultat est inscriptible et les modifications


seront rcrites dans le fichier un certain moment. Sachez que dautres programmes ayant fait
concord le mme fichier peuvent ne pas voir immdiatement ces changements. Le comportement exact de la concordance simultane des fichiers par plusieurs programmes dpend du
systme dexploitation.

FileChannel.MapMode.PRIVATE. Le tampon de rsultat est inscriptible, mais toute modification


est prive ce tampon et ne pourra pas tre envoye au fichier.

Une fois que vous avez obtenu le tampon, vous pouvez lire et crire des donnes laide des mthodes
de la classe ByteBuffer et de la superclasse Buffer.
Les tampons prennent en charge laccs aux donnes squentielles et alatoires. Un tampon a une
position avance par les oprations get et put. Vous pouvez par exemple parcourir squentiellement
tous les octets du tampon, comme suit :
while (buffer.hasRemaining())
{
byte b = buffer.get();
. . .
}

Vous pouvez galement utiliser laccs alatoire :


for (int i = 0; i < buffer.limit(); i++)
{
byte b = buffer.get(i);
. . .
}

Vous pouvez lire et crire des tableaux doctets avec les mthodes
get(byte[] bytes)
get(byte[], int offset, int length)

Il y a enfin les mthodes


getInt
getLong
getShort
getChar
getFloat
getDouble

pour lire les valeurs de type primitif stockes sous forme de valeurs binaires dans le fichier.
Comme nous lavons indiqu, Java utilise lordre "big-endian" pour les donnes binaires. Toutefois, si vous devez traiter un fichier contenant des nombres binaires dans lordre "little-endian",
appelez simplement
buffer.order(ByteOrder.LITTLE_ENDIAN);

Livre Java .book Page 774 Jeudi, 25. novembre 2004 3:04 15

774

Au cur de Java 2 - Notions fondamentales

Pour retrouver lordre actuel des octets, appelez


ByteOrder b = buffer.order()

ATTENTION
Ces deux mthodes nutilisent pas la convention de dnomination set/get.

Pour crire des nombres dans un tampon, utilisez lune des mthodes suivantes :
putInt
putLong
putShort
putChar
putFloat
putDouble

LExemple 12.8 calcule la somme de contrle de la redondance cyclique 32 bits (CRC32) dun
fichier. Ce rsultat est une somme de contrle souvent employe pour dterminer si un fichier a t
corrompu. Lorsquun fichier est corrompu, cela implique trs souvent que la somme de contrle a
chang. Le package java.util.zip contient une classe CRC32 qui calcule la somme de contrle
dune suite doctets, utilisant la boucle suivante :
CRC32 crc = new CRC32();
while (dautres octets)
crc.update(next byte)
long checksum = crc.getValue();

INFO
Pour obtenir une bonne explication de lalgorithme CRC, voir http://www.relisoft.com/Science/CrcMath.html.

Les dtails du calcul CRC importent peu. Nous lutilisons simplement en exemple dune opration
de fichier utile.
Excutez le programme avec
java NIOTest nomfichier

Exemple 12.8 : NIOTest.java


import
import
import
import

java.io.*;
java.nio.*;
java.nio.channels.*;
java.util.zip.*;

/**
Ce programme calcule la somme de contrle CRC dun fichier.
Usage: java NIOTest nomFichier
*/
public class NIOTest
{
public static long checksumInputStream(String filename)
throws IOException
{
InputStream in = new FileInputStream(filename);

Livre Java .book Page 775 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

CRC32 crc = new CRC32();


int c;
while((c = in.read())!= -1)
crc.update(c);
return crc.getValue();
}
public static long checksumBufferedInputStream(String filename)
throws IOException
{
InputStream in = new BufferedInputStream(
new FileInputStream(filename));
CRC32 crc = new CRC32();
int c;
while((c = in.read())!= -1)
crc.update(c);
return crc.getValue();
}
public static long checksumRandomAccessFile(String filename)
throws IOException
{
RandomAccessFile file = new RandomAccessFile(filename, "r");
long length = file.length();
CRC32 crc = new CRC32();
for (long p = 0; p < length; p++)
{
file.seek(p);
int c = file.readByte();
crc.update(c);
}
return crc.getValue();
}
public static long checksumMappedFile(String filename)
throws IOException
{
FileInputStream in = new FileInputStream(filename);
FileChannel channel = in.getChannel();
CRC32 crc = new CRC32();
int length = (int) channel.size();
MappedByteBuffer buffer =
channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
for (int p = 0; p < length; p++)
{
int c = buffer.get(p);
crc.update(c);
}
return crc.getValue();
}

775

Livre Java .book Page 776 Jeudi, 25. novembre 2004 3:04 15

776

Au cur de Java 2 - Notions fondamentales

public static void main(String[] args)


throws IOException
{
System.out.println("Input Stream:");
long start = System.currentTimeMillis();
long crcValue = checksumInputStream(args[0]);
long end = System.currentTimeMillis();
System.out.println(Long.toHexString(crcValue));
System.out.println((end - start) + " milliseconds");
System.out.println("Buffered Input Stream:");
start = System.currentTimeMillis();
crcValue = checksumBufferedInputStream(args[0]);
end = System.currentTimeMillis();
System.out.println(Long.toHexString(crcValue));
System.out.println((end - start) + " milliseconds");
System.out.println("Random Access File:");
start = System.currentTimeMillis();
crcValue = checksumRandomAccessFile(args[0]);
end = System.currentTimeMillis();
System.out.println(Long.toHexString(crcValue));
System.out.println((end - start) + " milliseconds");
System.out.println("Mapped File:");
start = System.currentTimeMillis();
crcValue = checksumMappedFile(args[0]);
end = System.currentTimeMillis();
System.out.println(Long.toHexString(crcValue));
System.out.println((end - start) + " milliseconds");
}
}
java.io.FileInputStream 1.0

FileChannel getChannel() 1.4

Renvoie un canal pour accder ce flux.


java.io.FileOutputStream 1.0

FileChannel getChannel() 1.4

Renvoie un canal pour accder ce flux.


java.io.RandomAccessFile 1.0

FileChannel getChannel() 1.4

Renvoie un canal pour accder ce fichier.


java.nio.channels.FileChannel 1.4

MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)

Fait concorder une rgion du fichier avec la mmoire.


Paramtres :

mode

Lune des constantes READ_ONLY, READ_WRITE ou PRIVATE


dans la classe FileChannel.MapMode.

position

Le dbut de la rgion mise en concordance.

size

La taille de la rgion mise en concordance.

Livre Java .book Page 777 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

777

java.nio.Buffer 1.4

boolean hasRemaining()

Renvoie true si la position actuelle du tampon na pas encore atteint la position limite du
tampon.

int limit()

Renvoie la position limite du tampon, savoir la premire position laquelle il ny a plus de


valeurs disponibles.
java.nio.ByteBuffer 1.4

byte get()

Rcupre un octet partir de la position actuelle et avance la position au prochain octet.

byte get(int index)

Rcupre un octet partir de lindice spcifi.

ByteBuffer put(byte b)

Place un octet la position actuelle et avance la position au prochain octet. Renvoie une rfrence ce tampon.

ByteBuffer put(int index, byte b)

Place un octet lindice spcifi. Renvoie une rfrence ce tampon.

ByteBuffer get(byte[] destination)


ByteBuffer get(byte[] destination, int offset, int length)

Remplissent un tableau doctets ou une rgion dun tableau doctets par des octets du tampon et
avancent la position actuelle du nombre doctets lus. Sil ne reste plus suffisamment doctets
dans le tampon, aucun octet nest lu et le systme dclenche une exception BufferUnderflowException. Renvoient une rfrence ce tampon.
Paramtres :

destination

Le tableau doctets remplir.

offset

Le dcalage de la rgion remplir.

length

La longueur de la rgion remplir.

ByteBuffer put(byte[] source)


ByteBuffer put(byte[] source, int offset, int length)

Placent tous les octets dun tableau doctets ou les octets dune rgion dun tableau doctets dans
le tampon et avancent la position actuelle du nombre doctets lus. Sil ne reste plus suffisamment
doctets dans le tampon, aucun octet nest crit et le systme dclenche une exception BufferOverflowException. Renvoient une rfrence ce tampon.
Paramtres :

source

Le tableau doctets crire.

offset

Le dcalage de la rgion crire.

length

La longueur de la rgion crire.

Xxx getXxx()
Xxx getXxx(int index)
ByteBuffer putXxx(xxx value)

Livre Java .book Page 778 Jeudi, 25. novembre 2004 3:04 15

778

Au cur de Java 2 - Notions fondamentales

ByteBuffer putXxx(int index, xxx value)

Ces mthodes sont utilises pour la lecture et lcriture absolues et relatives de nombres binaires.
Xxx est de type Int, Long, Short, Char, Float ou Double.

ByteBuffer order(ByteOrder order)


ByteOrder order()

Dfinissent ou rcuprent lordre des octets. La valeur de lordre est lune des constantes BIG_
ENDIAN ou LITTLE_ENDIAN de la classe ByteOrder.

La structure des donnes du tampon


Lorsque vous utilisez la concordance de mmoire, vous crez un seul tampon qui stend sur la totalit du fichier ou sur la zone du fichier qui vous intresse. Vous pouvez galement utiliser des
tampons pour lire et crire des tronons dinformations plus modestes.
Dans cette section, nous dcrirons brivement les oprations de base ralises sur les objets Buffer.
Un tampon est un tableau de valeurs du mme type. La classe Buffer est une classe abstraite avec
les sous-classes concrtes ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer,
LongBuffer et ShortBuffer. Les deux sous-classes les plus usites dans la pratique sont ByteBuffer
et CharBuffer. Comme indiqu la Figure 12.13, un tampon possde :
m

une capacit qui ne change jamais ;

une position laquelle est lue ou crite la prochaine valeur ;

une limite au-del de laquelle lcriture ou la lecture deviennent inutiles ;

en option, une marque permettant de rpter une opration de lecture ou dcriture.

Ces valeurs remplissent les conditions :


0 marque position limite capacit

Figure 12.13

Dj crites/lues

Pas encore crites/lues

Hors limites

Un tampon

Marque

Position

Remaining

Limite

Capacit

Le principal objectif dun tampon est un cycle "criture, puis lecture". Au dpart, sa position est
0 et la limite se cale sur sa capacit. Il faut constamment appeler put pour lui ajouter des
valeurs. Lorsque vous avez atteint la fin des donnes ou la limite (la capacit), il est temps de
passer la lecture.
Appelez flip pour dfinir la limite sur la position courante et la position sur 0. Appelez constamment get tant que la mthode remaining (qui renvoie limite - position) est positive. Lorsque vous
avez lu toutes les valeurs du tampon, appelez clear pour le prparer au prochain cycle dcriture.
La mthode clear rinitialise la position 0 et la limite sur la capacit.
Pour relire le tampon, utilisez rewind ou mark/reset (consultez les notes API pour en savoir plus).

Livre Java .book Page 779 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

779

java.nio.Buffer 1.4

Buffer clear()

Prpare ce tampon lcriture en dfinissant la position sur zro et la limite sur la capacit ;
renvoie this.

Buffer flip()

Prpare ce tampon la lecture en dfinissant la limite sur la position et la position sur zro ;
renvoie this.

Buffer rewind()

Prpare ce tampon la relecture des mmes valeurs en dfinissant la position sur zro et en laissant
la limite inchange ; renvoie this.

Buffer mark()

Dfinit la marque de ce tampon sur la position ; renvoie this.

Buffer reset()

Dfinit la position de ce tampon sur la marque, ce qui permet la portion marque dtre relue
ou rcrite ; renvoie this.

int remaining()

Renvoie le nombre restant de valeurs lisibles ou inscriptibles, savoir la diffrence entre la limite
et la position.

int position()

Renvoie la position de ce tampon.

int capacity()

Renvoie la capacit de ce tampon.


java.nio.CharBuffer 1.4

char get()
CharBuffer get(char[] destination)
CharBuffer get(char[] destination, int offset, int length)

Rcuprent une valeur char ou une plage de valeurs char, en commenant la position du
tampon et en la dplaant aprs les caractres qui ont t lus. Les deux dernires mthodes
renvoient this.

CharBuffer put(char c)
CharBuffer put(char[] source)
CharBuffer put(char[] source, int offset, int length)
CharBuffer put(String source)
CharBuffer put(CharBuffer source)

Placent une valeur char ou une plage de valeurs char, en commenant la position du tampon
et en la dplaant aprs les caractres qui ont t crits. Lors de la lecture depuis un CharBuffer,
tous les caractres restants sont lus. Toutes les mthodes renvoient this.

CharBuffer read(CharBuffer destination)

Rcupre les valeurs char de ce tampon et les place dans la cible jusqu atteindre la limite de la
cible. Renvoie this.

Livre Java .book Page 780 Jeudi, 25. novembre 2004 3:04 15

780

Au cur de Java 2 - Notions fondamentales

Verrouillage des fichiers


Envisagez une situation dans laquelle plusieurs programmes sexcutant simultanment doivent
modifier le mme fichier. A lvidence, les programmes doivent communiquer entre eux, dune
manire ou dune autre, faute de quoi le fichier peut facilement tre endommag.
Les verrous de fichiers peuvent permettre de contrler laccs un fichier ou une plage doctets
dans un fichier. Toutefois, le verrouillage du fichier varie considrablement selon les systmes
dexploitation, ce qui explique labsence des capacits de verrouillage des versions prcdentes du
JDK.
Pour parler franchement, le verrouillage des fichiers nest pas si commun dans la programmation des
applications. Beaucoup utilisent une base de donnes pour le stockage des donnes ; la base de
donnes possde aussi des mcanismes permettant de rsoudre les problmes daccs simultan. Si
vous stockez des informations dans ces fichiers et que vous soyez proccup par laccs simultan,
il sera peut-tre plus simple de commencer utiliser une base de donnes plutt que de concevoir
des schmas complexes de verrouillage de fichiers.
Pourtant, dans certaines situations, ce verrouillage se rvle essentiel. Supposons que votre application enregistre un fichier de configuration contenant des prfrences utilisateur. Si un utilisateur
appelle deux instances de lapplication, il se pourrait que les deux souhaitent crire le fichier de
configuration au mme moment. Dans ce cas, la premire instance devrait verrouiller le fichier. Lorsque la deuxime trouve le fichier verrouill, elle peut dcider de patienter jusqu son dverrouillage
ou simplement ignorer le processus dcriture.
Pour verrouiller un fichier, appelez la mthode lock ou tryLock de la classe FileChannel:
FileLock lock = channel.lock();

ou
FileLock lock = channel.tryLock();

Le premier appel se bloque jusqu ce que le verrou devienne disponible. Le second renvoie immdiatement soit le verrou, soit null si le verrou nest pas disponible. Le fichier reste verrouill jusqu
ce que le canal soit ferm ou que la mthode release soit appele sur le verrou.
Vous pouvez aussi verrouiller une partie du fichier avec lappel
FileLock lock(long start, long size, boolean exclusive)

ou
FileLock tryLock(long start, long size, boolean exclusive)

Le drapeau exclusive vaut true pour verrouiller le fichier en lecture et criture. Il est false pour
un verrou partag, permet plusieurs processus de lire partir du fichier, tout en empchant les
processus dacqurir un verrou exclusive. Tous les systmes dexploitation ne prennent pas en
charge les verrous partags. Vous risquez dobtenir un verrou exclusive mme si vous venez den
demander un qui soit partag. Appelez la mthode isShared de la classe FileLock pour dcouvrir
le type dont vous disposez.

Livre Java .book Page 781 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

781

INFO
Si vous verrouillez la portion de queue dun fichier et que le fichier crot ensuite au-del de cette portion, la partie
supplmentaire ne sera pas verrouille. Pour verrouiller tous les octets, utilisez une taille de Long.MAX_VALUE.

Noubliez pas que le verrouillage des fichiers dpend du systme. Voici quelques aspects considrer :
m

Sur certains systmes, le verrouillage des fichiers na quun but indicatif. Si une application ne
parvient pas rcuprer un verrou, elle peut toujours crire dans un fichier quune autre application
a actuellement verrouille.

Sur certains systmes, vous ne pouvez pas simultanment verrouiller un fichier et le faire concorder
en mmoire.

Les verrous de fichiers sont contenus dans la machine virtuelle Java complte. Si deux programmes sont lancs par la mme machine virtuelle (cet applet ou ce lanceur dapplication), ils
peuvent chacun acqurir un verrou sur le mme fichier. Les mthodes lock et tryLock dclencheront une exception OverlappingFileLockException si la machine virtuelle dtient dj un
autre verrou en chevauchement sur le mme fichier.

Sur certains systmes, fermer un canal libre tous les verrous sur le fichier sous-jacent dtenu par
la machine virtuelle Java. Evitez donc davoir plusieurs canaux sur le mme fichier verrouill.

Verrouiller des fichiers sur un systme de fichiers en rseau dpend beaucoup du systme ; il
convient donc de lviter.

java.nio.channels.FileChannel 1.4

FileLock lock()

Acquiert un verrou exclusive sur la totalit du fichier. Cette mthode bloque jusqu ce que le
verrou soit acquis.

FileLock tryLock()

Acquiert un verrou exclusive sur la totalit du fichier ou renvoie null si le verrou ne peut pas
tre acquis.

FileLock lock(long position, long size, boolean shared)


FileLock tryLock(long position, long size, boolean shared)

Ces mthodes acquirent un verrou sur une rgion du fichier. La premire mthode se bloque
jusqu ce que le verrou soit acquis et la deuxime mthode renvoie null si le verrou ne peut pas
tre acquis.
Paramtres :

position

Le dbut de la rgion verrouiller.

size

La taille de la rgion verrouiller.

shared

true pour un verrou partag, false pour un verrou exclusif.

java.nio.channels.FileLock 1.4

void release()

Libre ce verrou.

Livre Java .book Page 782 Jeudi, 25. novembre 2004 3:04 15

782

Au cur de Java 2 - Notions fondamentales

Expressions ordinaires
Les expressions ordinaires permettent de spcifier des motifs de chanes. Vous pouvez utiliser des
expressions ordinaires ds que vous devez situer des chanes qui concordent avec un motif particulier. Lun de nos exemples de programme retrouve tous les liens hypertexte dun fichier HTML en
recherchant les chanes ayant le motif <a href="">.
Bien entendu, lorsque vous spcifiez un motif, la notation nest pas suffisamment prcise. Vous
devez spcifier prcisment la suite de caractres qui constituera une concordance autorise. Il existe
une syntaxe spciale utiliser pour dcrire un motif.
En voici un exemple simple. Lexpression ordinaire
[Jj]ava.+

correspond nimporte quelle chane ayant la forme suivante :


m

La premire lettre est un J ou un j.

Les trois lettres suivantes sont ava.

Le reste de la chane est constitu dun ou de plusieurs caractres arbitraires.

Par exemple, la chane "javanais" correspond cette expression, la diffrence de la chane "Au
coeur de Java".
Il vous faut donc connatre un peu la syntaxe pour comprendre la signification dune expression ordinaire. Heureusement, dans la plupart des cas, quelques constructions simples suffisent.
m

Une classe de caractres est un jeu de caractres alternatifs, entour de crochets, comme [Jj],
[0-9], [A-Za-z] ou [^0-9]. Ici, le traduit une plage de caractres (tous ceux dont la valeur
Unicode se trouve entre les deux limites) et ^ indique le complment (tous les caractres sauf
ceux spcifis).

Il existe de nombreuses classes de caractres prdfinies comme \d (chiffres) ou \p{Sc}


(symbole de devise Unicode). Reportez-vous aux Tableaux 12.8 et 12.9.

La plupart des caractres sont dsigns par eux-mmes, comme dans lexemple prcdent des
caractres ava.

Le symbole . remplace nimporte quel caractre (sauf les terminaisons de lignes, selon les paramtres).

Utilisez \ comme caractre dchappement, par exemple \. correspond un point et \\ une


barre oblique inverse.

^ et $ correspondent respectivement au dbut et la fin dune ligne.

Si X et Y sont des expressions ordinaires, XY signale "toute correspondance pour X suivie dune
correspondance pour Y". X | Y signale "toute correspondance pour X ou Y".

Vous pouvez appliquer des quantifieurs X+ (1 ou plus), X* (0 ou plus) et X? (0 ou 1) une


expression X.

Par dfaut, un quantifieur correspond la plus grande rptition possible qui fait russir une
correspondance gnrale. Vous pouvez modifier ce comportement avec les suffixes ? (correspondance faite sans enthousiasme ou insuffisante, concordance de plus petit nombre de rptitions)

Livre Java .book Page 783 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

783

et + (correspondance possessive ou gourmande, correspondance du plus grand nombre, mme si


elle fait chouer la concordance gnrale).
m

Par exemple, la chane cab correspond [a-z]*ab, mais pas [a-z]*+ab. Dans le premier cas,
lexpression [a-z]* ne correspond quau caractre c, de sorte que les caractres ab correspondent au reste du motif. Or la version gourmande [a-z]*+ correspond aux caractres cab, ce qui
laisse le reste du motif sans correspondance.

Vous pouvez utiliser des groupes pour dfinir des sous-expressions. Entourez les groupes par des
( ), par exemple ([+-]?)([0-9]+). Vous pouvez ensuite demander au systme de correspondance des motifs de renvoyer la correspondance de chaque groupe ou de se reporter un groupe
avec \n, o n est le numro du groupe (qui commence par \1).

Voici par exemple une expression ordinaire quelque peu complexe mais qui peut se rvler trs utile,
elle dcrit des entiers dcimaux ou hexadcimaux :
[+-]?[0-9]+|0[Xx][0-9A-Fa-f]+

Malheureusement, la syntaxe de lexpression nest pas totalement standardise entre les divers
programmes et bibliothques qui utilisent les expressions ordinaires. Mme sil existe un consensus
sur les constructions de base, de nombreuses diffrences ennuyeuses demeurent quant aux dtails.
Les classes dexpressions ordinaires Java utilisent une syntaxe semblable celle utilise dans le
langage Perl, mme si elle nest pas tout fait identique. Le Tableau 12.8 montre toutes les constructions de la syntaxe Java. Pour en savoir plus sur la syntaxe des expressions ordinaires, consultez la
documentation API de la classe Pattern dans louvrage Matrise des expressions rgulires de
Jeffrey E. F. Friedl (OReilly, 2003).
La plus simple utilisation dune expression ordinaire consiste tester si une chane particulire lui
correspond. Voici comment programmer ce test en Java. Construisez dabord un objet Pattern
partir de la chane montrant lexpression ordinaire. Rcuprez ensuite un objet Matcher partir du
motif, puis appelez sa mthode matches:
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(input);
if (matcher.matches()) . . .

Tableau 12.8 : Syntaxe des expressions ordinaires

Syntaxe

Explication

Caractres
c

Le caractre c.

\unnnn, \xnn, \0nn, \0nnn

Lunit de code avec la valeur octale ou hexadcimale donne.

\t, \n, \r, \f, \a, \e

Les caractres de contrle tabulation, nouvelle ligne, retour,


retour chariot, alerte et chappement.

\cc

Le caractre de contrle correspondant au caractre c.

Livre Java .book Page 784 Jeudi, 25. novembre 2004 3:04 15

784

Au cur de Java 2 - Notions fondamentales

Tableau 12.8 : Syntaxe des expressions ordinaires (suite)

Syntaxe

Explication

Classes de caractres
[C1C2]

Lun des caractres reprsents par C1, C2, ... Les Ci sont des
caractres, des plages de caractres (c1-c2) ou des classes de
caractres.

[^]

Complment de la classe de caractres.

[&&]

Intersection de deux classes de caractres.

Classes de caractres prdfinies


.

Tout caractre sauf des terminaisons de lignes (ou tout caractre


si le drapeau DOTALL est dfini).

\d

Un chiffre de [0-9].

\D

Un caractre non numrique [^0-9].

\s

Une espace [ \t\n\r\f\x0B].

\S

Un caractre autre que lespace.

\w

Un caractre de mot [a-zA-Z0-9_].

\W

Un caractre autre quun mot.

\p{nom}

Une classe de caractre nomme (voir Tableau 12.9).

\P{nom}

Le complment dune classe de caractre nomme.

Correspondances de limites
^ $

Dbut, fin dune entre (ou dbut, fin de ligne en mode multiligne).

\b

Une limite de mot.

\B

Une limite pour autre chose quun mot.

\A

Dbut dune saisie.

\z

Fin dune saisie.

\Z

Fin dune saisie sauf terminaison de dernire ligne.

\G

Fin de la prcdente correspondance.

Livre Java .book Page 785 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

785

Tableau 12.8 : Syntaxe des expressions ordinaires (suite)

Syntaxe

Explication

Quantifieurs
X?

X optionnel.

X*

X, 0 ou plusieurs fois.

X+

X, 1 ou plusieurs fois.

X{n} X{n,} X{n,m}

X n fois, au moins n fois, entre n et m fois.

Suffixes des quantifieurs


?

Transforme la concordance par dfaut (gourmande) en correspondance sans enthousiasme.

Transforme la concordance par dfaut (gourmande) en correspondance possessive.

Oprations des jeux


XY

Toute chane de X, suivie par toute chane de Y.

X|Y

Toute chane de X ou Y.

Groupage
(X)

Capture la chane correspondant X sous forme de groupe.

\n

La correspondance du nime groupe.

Echappements
\c

Le caractre c (ne doit pas tre un caractre alphabtique).

\Q\E

Citation mot pour mot.

(?...)

Construction spciale (voir notes API de la classe Pattern).

Tableau 12.9 : Noms des classes de caractres prdfinis

Lower

Minuscule ASCII [a-z]

Upper

Majuscule ASCII [A-Z]

Alpha

ASCII alphabtique [A-Za-z]

Digit

Chiffre ASCII [0-9]

Livre Java .book Page 786 Jeudi, 25. novembre 2004 3:04 15

786

Au cur de Java 2 - Notions fondamentales

Tableau 12.9 : Noms des classes de caractres prdfinis (suite)

Alnum

Lettre ou chiffre ASCII [A-Za-z0-9]

XDigit

Chiffres hexadcimaux [0-9A-Fa-f]

Print ou Graph

Caractre ASCII imprimable [\x21-\x7E]

Punct

Chiffre ou caractre non alphabtique ASCII [\p{Print}&&\


P{Alnum}]

ASCII

Tous les caractres ASCII [\x00-\x7F]

Cntrl

Caractre de contrle ASCII [\x00-\x1F]

Blank

Espace ou tabulation [ \t]

Space

Espace [ \t\n\r\f\0x0B]

javaLowerCase

Minuscule, comme indiqu par Character.isLowerCase()

javaUpperCase

Majuscule, comme indiqu par Character.isUpperCase()

javaWhitespace

Espace, comme indiqu par Character.isWhitespace()

javaMirrored

Mis en miroir, comme indiqu par Character.isMirrored()

InBloc

Bloc est le nom dun bloc de caractres Unicode, avec suppression des espaces, comme BasicLatin ou Mongolian. Voir http://
www.unicode.org pour obtenir une liste des noms de blocs

Catgorie ou InCatgorie

Catgorie est le nom dune catgorie de caractres Unicode


comme L (lettre) ou Sc (symbole de devise). Voir http://unicode.org pour obtenir la liste des noms de catgories

Lentre dun caractre correspondant est un objet de toute classe qui implmente linterface CharSequence, comme String, StringBuilder ou CharBuffer.
Lorsque vous compilez le motif, vous pouvez dfinir une ou plusieurs balises, par exemple
Pattern pattern = Pattern.compile(patternString,
Pattern.CASE_INSENSITIVE + Pattern.UNICODE_CASE);

Les six balises suivantes sont prises en charge :


m

CASE_INSENSITIVE. Fait correspondre les caractres indpendamment de la casse. Par dfaut,


cette balise ne prend en compte que les caractres ASCII US.

UNICODE_CASE. Lorsquelle est combine CASE_INSENSITIVE, utilise la casse des lettres


Unicode pour la correspondance.

MULTILINE. ^ et $ correspondent au dbut et la fin dune ligne et non la totalit de lentre.

UNIX_LINES. seul "\n" est reconnu comme terminaison de ligne lors dune correspondance avec
^ et $ en mode multiligne.

Livre Java .book Page 787 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

787

DOTALL. Lorsque vous utilisez cette balise, le symbole . correspond tous les caractres, y
compris les fins de ligne.

CANON_EQ. Prend en compte lquivalent canonique des caractres Unicode. Par exemple, u suivi
de (trma) correspond .

Si lexpression ordinaire contient des groupes, lobjet Matcher peut indiquer les limites du groupe.
Les mthodes
int start(int groupIndex)
int end(int groupIndex)

produisent lindice de dpart et lindice de dpassement de fin dun groupe particulier.


Vous pouvez simplement extraire la chane en correspondance en appelant
String group(int groupIndex)

Le groupe 0 correspond la totalit de la saisie. Son indice pour le premier groupe rel est 1. Appelez
la mthode groupCount pour rcuprer le nombre total de groupes.
Les groupes imbriqus sont ordonns par les parenthses ouvrantes. Par exemple, au vu du motif
((1?[0-9]):([0-5][0-9]))[ap]m

et de lentre
11:59am

le systme de correspondance rapporte les groupes suivants


Indice du groupe

Dbut

Fin

Chane

11:59am

11:59

11

59

LExemple 12.9 appelle un motif, puis les chanes mettre en correspondance. Il indique si les chanes de saisie correspondent ou non au motif. Si cest le cas et que le motif contienne des groupes, le
programme affiche les limites du groupe sous forme de parenthses, comme
((11):(59))am

Exemple 12.9 : RegexTest.java


import java.util.*;
import java.util.regex.*;
/**
Ce programme teste la correspondance dexpressions ordinaires.
Entrez un motif et les chanes faire concorder ou appuyez sur
Cancel pour le fermer. Si le motif contient des groupes, les
limites du groupe saffichent dans la correspondance.
*/
public class RegExTest
{

Livre Java .book Page 788 Jeudi, 25. novembre 2004 3:04 15

788

Au cur de Java 2 - Notions fondamentales

public static void main(String[] args)


{
Scanner in = new Scanner(System.in);
System.out.println("Enter pattern: ");
String patternString = in.nextLine();
Pattern pattern = null;
try
{
pattern = Pattern.compile(patternString);
}
catch (PatternSyntaxException e)
{
System.out.println("Pattern syntax error");
System.exit(1);
}
while (true)
{
System.out.println("Enter string to match: ");
String input = in.nextLine();
if (input == null || input.equals("")) return;
Matcher matcher = pattern.matcher(input);
if (matcher.matches())
{
System.out.println("Match");
int g = matcher.groupCount();
if (g > 0)
{
for (int i = 0; i < input.length(); i++)
{
for (int j = 1; j <= g; j++)
if (i == matcher.start(j))
System.out.print(();
System.out.print(input.charAt(i));
for (int j = 1; j <= g; j++)
if (i + 1 == matcher.end(j))
System.out.print());
}
System.out.println();
}
}
else
System.out.println("No match");
}
}
}

Gnralement, il nest pas ncessaire de faire correspondre la totalit de la saisie avec une expression
ordinaire, mais vous souhaiterez peut-tre retrouver une ou plusieurs sous-chanes correspondantes dans
la saisie. Utilisez la mthode find de la classe Matcher pour retrouver la prochaine correspondance. Si
elle renvoie true, utilisez les mthodes start et end pour retrouver ltendue de la correspondance :
while (matcher.find())
{
int start = matcher.start();
int end = matcher.end();
String match = input.substring(start, end);
. . .
}

Livre Java .book Page 789 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

789

LExemple 12.10 fait fonctionner ce mcanisme. Il retrouve toutes les rfrences des liens hypertexte dans une page Web et les affiche. Pour excuter le programme, fournissez une URL sur la ligne
de commande, comme suit :
java HrefMatch http://www.horstmann.com

Exemple 12.10 : HrefMatch.java


import java.io.*;
import java.net.*;
import java.util.regex.*;
/**
Ce programme affiche toutes les URL dune page Web par
correspondance dune expression ordinaire qui dcrit la
balise HTML <a href=...>. Lance le programme comme
java HrefMatch URL
*/
public class HrefMatch
{
public static void main(String[] args)
{
try
{
// rcuprer la chane URL partir de la ligne de commande ou
// utiliser celle par dfaut
String urlString;
if (args.length > 0) urlString = args[0];
else urlString = "http://java.sun.com";
// ouvrir le lecteur pour lURL
InputStreamReader in = new InputStreamReader(
new URL(urlString).openStream());
// lire le contenu dans le tampon des chanes
StringBuilder input = new StringBuilder();
int ch;
while ((ch = in.read())!= -1) input.append((char) ch);
// rechercher toutes les occurrences du motif
String patternString =
"<a\\s+href\\s*=\\s*(\"[^\"]*\"|[^\\s>])\\s*>";
Pattern pattern = Pattern.compile(
patternString, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(input);
while (matcher.find())
{
int start = matcher.start();
int end = matcher.end();
String match = input.substring(start, end);
System.out.println(match);
}
}
catch (IOException e)
{
e.printStackTrace();
}
catch (PatternSyntaxException e)

Livre Java .book Page 790 Jeudi, 25. novembre 2004 3:04 15

790

Au cur de Java 2 - Notions fondamentales

{
e.printStackTrace();
}
}
}

La mthode replaceAll de la classe Matcher remplace toutes les occurrences dune expression
ordinaire par une chane de remplacement. Les instructions suivantes, par exemple, remplacent
toutes les suites de chiffres par un caractre #:
Pattern pattern = Pattern.compile("[0-9]+");
Matcher matcher = pattern.matcher(input);
String output = matcher.replaceAll("#");

La chane de remplacement peut contenir des rfrences aux groupes dans le motif : $n est remplac
par le nime groupe. Utilisez \$ pour inclure un caractre $ dans le texte de remplacement.
Il existe aussi une mthode replaceFirst qui ne remplace que la premire occurrence du motif.
Enfin, la classe Pattern possde une mthode split qui fonctionne comme un tokenizer de chane.
Elle divise une entre en un tableau de chanes dans lequel les correspondances dexpressions ordinaires
serviront de frontires. Par exemple, les instructions suivantes divisent lentre en jetons (tokens) dans
lesquels les dlimiteurs sont des points de ponctuation entours dune espace optionnelle :
Pattern pattern = Pattern.compile("\\s*\\p{Punct}\\s*");
String[] tokens = pattern.split(input);
java.util.regex.Pattern 1.4
static Pattern compile(String expression)
static Pattern compile(String expression, int flags)

Compilent la chane de lexpression ordinaire en un objet de motif pour un traitement rapide des
correspondances.
Paramtres :

expression

Lexpression ordinaire.

flags

Un ou plusieurs des drapeaux CASE_INSENSITIVE,


UNICODE_CASE, MULTILINE, UNIX_LINES, DOTALL
et CANON_EQ.

Matcher matcher(CharSequence input)

Renvoie un objet matcher que vous pouvez utiliser pour retrouver les correspondances du motif
dans la saisie.

String[] split(CharSequence input)


String[] split(CharSequence input, int limit)

Divisent la chane de saisie en jetons dans lesquels le motif spcifie la forme des dlimiteurs.
Renvoient un tableau de jetons. Les dlimiteurs ne font pas partie des jetons.
Paramtres :

input

La chane diviser en jetons.

limit

Le nombre maximal de chanes produire.


Si lon a trouv des dlimiteurs de correspondance
limit -1, la dernire entre du tableau renvoy
contient lentre restante non divise.
Si limit est infrieur ou gal 0, la chane entire est
divise. Si limit gale 0, les premires chanes vides
ne sont pas places dans le tableau renvoy.

Livre Java .book Page 791 Jeudi, 25. novembre 2004 3:04 15

Chapitre 12

Les flux et les fichiers

791

java.util.regex.Matcher 1.4

boolean matches()

Renvoie true si la saisie correspond au motif.

boolean lookingAt()

Renvoie true si le dbut de la saisie correspond au motif.

boolean find()
boolean find(int start)

Tentent de retrouver la correspondance suivante et renvoient true si une autre correspondance


est trouve.
Paramtres :

start

Le dbut de lindice auquel commencer la recherche.

int start()
int end()

Renvoient le dbut et la position au-del de la fin de la correspondance courante.

String group()

Renvoie la correspondance actuelle.

int groupCount()

Renvoie le nombre de groupes dans le motif dentre.

int start(int groupIndex)


int end(int groupIndex)

Renvoient le dbut et la position au-del de la fin dun groupe donn dans la correspondance
actuelle.
Paramtres :

groupIndex

Lindice du groupe (commenant par 1) ou 0 pour indiquer


la totalit de la correspondance.

String group(int groupIndex)

Renvoie la correspondance de chane dun groupe donn.


Paramtres :

groupIndex

Lindice du groupe (commenant par 1) ou 0 pour indiquer


la totalit de la correspondance.

String replaceAll(String replacement)


String replaceFirst(String replacement)

Renvoient une chane obtenue de lentre matcher en remplaant toutes les correspondances ou
la premire par la chane de remplacement.
Paramtres :

replacement

La chane de remplacement. Peut contenir des rfrences


un groupe de motifs comme $n. Utilisez \$ pour inclure un
symbole $.

Matcher reset()
Matcher reset(CharSequence input)

Rinitialisent ltat du matcher. La seconde mthode applique le matcher une saisie diffrente.
Les deux mthodes renvoient this.

Livre Java .book Page 792 Jeudi, 25. novembre 2004 3:04 15

Livre Java .book Page 793 Jeudi, 25. novembre 2004 3:04 15

13
Programmation gnrique
Au sommaire de ce chapitre

Pourquoi la programmation gnrique ?


Dfinition dune classe gnrique simple
Mthodes gnriques
Liaisons pour variables de type
Code gnrique et machine virtuelle
Restrictions et limites
Rgles dhritage pour les types gnriques
Types joker
La rflexion et le systme gnrique
La programmation gnrique constitue la nouveaut la plus significative du langage de programmation Java depuis la version 1.0. Lapparition du gnrique dans le JDK 5.0 rsulte de lune des
premires requtes de spcifications Java, JSR 14, produite en 1999. Le groupe dexperts a pass
environ cinq ans travailler sur des spcifications et tester des implmentations.
Le gnrique tait fort attendu car il permet dcrire un code plus sr et plus facile lire quun code
parsem de variables Object et de transtypages. Il est tout particulirement utile pour les classes de
collection, comme ArrayList.
Le gnrique est, du moins en apparence, semblable aux modles de C++. En C++, comme en Java,
les modles ont dabord t ajouts au langage pour soutenir les collections lourdes en types. Malgr
tout, au cours des annes, dautres utilisations ont t dcouvertes. Lorsque vous aurez lu ce chapitre,
vous en trouverez peut-tre encore dautres.

Livre Java .book Page 794 Jeudi, 25. novembre 2004 3:04 15

794

Au cur de Java 2 - Notions fondamentales

Pourquoi la programmation gnrique ?


La programmation gnrique implique dcrire du code qui puisse tre rutilis pour des objets de
types diffrents. Il nest pas utile, par exemple, de programmer des classes diffrentes pour collecter
les objets String et File, puisque la classe ArrayList collecte les objets de nimporte quelle
classe. Cest un exemple de programmation gnrique.
Avant le JDK 5.0, la programmation gnrique en Java sobtenait chaque fois par lhritage.
La classe ArrayList conservait simplement un tableau de rfrences Object:
public class ArrayList // avant JDK 5.0
{
public Object get(int i) { . . . }
public void add(Object o) { . . . }
. . .
private Object[] elementData;
}

Cette approche prsente deux problmes. Dune part, il faut avoir recours au transtypage lorsque
vous rcuprez une valeur :
ArrayList files = new ArrayList();
. . .
String filename = (String) names.get(0);

Dautre part, il nexiste aucune procdure de vrification des erreurs. Vous pouvez ajouter des
valeurs de nimporte quelle classe :
files.add(new File(". . ."));

Cet appel se compile et sexcute sans erreur. Partout ailleurs, transtyper le rsultat de get sur une
chane produira une erreur.
Le JDK 5.0 propose une meilleure solution : les paramtres de type. La classe ArrayList dispose
dsormais dun paramtre de type qui prcise le type dlment utilis :
ArrayList<String> files = new ArrayList<String>();

Votre code est alors plus facile lire. Vous voyez immdiatement que cet ArrayList contient des
objets String.
Le compilateur utilisera galement ces informations bon escient. Aucun transtypage nest ncessaire pour appeler get: le compilateur sait que le type de retour est String, et non Object:
String filename = files.get(0);

Il sait aussi que la mthode add dun ArrayList<String> possde un paramtre de type String.
Cela est bien plus sr que dutiliser un paramtre Object. Le compilateur peut alors vrifier que
vous ninsrez aucun objet dun type erron. Par exemple, linstruction
files.add(new File(". . .")); // ne peut ajouter que des objets String
un ArrayList<String>

ne se compilera pas. Dailleurs, une erreur de compilation vaut bien mieux quune exception de
transtypage de classe au moment de lexcution.
Cest lappel des paramtres de type : ils facilitent la lecture des programmes et les scurisent.

Livre Java .book Page 795 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

795

Y a-t-il un programmeur gnrique dans la salle ?


Lutilisation dune classe gnrique comme ArrayList se fait sans grande difficult. La plupart des
programmeurs Java utiliseront simplement des types tel ArrayList<String>, comme s"ils avaient
t intgrs dans le langage, linstar des tableaux String[] (bien sr, les ArrayList sont plus
intressants que les tableaux car ils peuvent grandir automatiquement).
Mais limplmentation dune classe gnrique nest pas aussi simple quil y parat. Les programmeurs qui utilisent votre code voudront y intgrer toutes sortes de classes pour vos paramtres de
type. Ils sattendent donc ce que tout fonctionne sans trop de restrictions ni messages derreurs
confus. Si vous lacceptez, votre mission, en tant que programmeur gnrique, consiste anticiper
toutes les utilisations potentielles de votre classe.
Oui, mais jusquo ? Cest l un problme ordinaire que rencontraient les concepteurs de la bibliothque de classes standard. La classe ArrayList possde une mthode addAll qui lui permet
dajouter tous les lments dune autre collection. Un programmeur peut vouloir ajouter tous les
lments dun ArrayList<Manager> un ArrayList<Employee>. Bien entendu, le contraire ne
doit pas tre autoris. Comment faire alors pour autoriser un appel et interdire lautre ? Les concepteurs du langage Java ont invent un nouveau concept ingnieux pour rsoudre ce problme, le type
joker. Les types joker sont plutt abstraits, mais ils permettent un concepteur de bibliothque de
rendre les mthodes aussi flexibles que possible.
La programmation gnrique aborde trois niveaux de comptences. Au niveau le plus bas, vous utilisez uniquement les classes gnriques, gnralement des collections comme ArrayList, sans
penser la manire dont elles fonctionnent ni leurs raisons dtre.
La plupart des programmeurs dapplications se contenteront de ce niveau jusqu ce quun problme
apparaisse.
Vous risquez de rencontrer un message derreur droutant lorsque vous mlangez diffrentes classes
gnriques ou que vous interfacez avec du code existant qui ne connat pas les paramtres de type. A
ce moment-l, il vous faudra complter votre culture du gnrique Java pour rsoudre les problmes
systmatiquement, plutt que ttonner chaque fois. Enfin, bien sr, vous voudrez peut-tre implmenter vos propres classes et mthodes gnriques.
Les programmeurs dapplications ncriront probablement pas de grosses quantits de code gnrique. Les quipes de Sun ont dj fait le plus gros et fourni des paramtres de type pour toutes les
classes de collection.
En rgle gnrale, seul le code qui impliquait gnralement beaucoup de transtypages de types trs
gnraux (comme Object ou linterface Comparable) profitera de lutilisation des paramtres de
type.
Dans ce chapitre, vous dcouvrirez tout ce quil faut savoir pour implmenter votre propre code
gnrique. Vous devrez toutefois utiliser ces connaissances principalement pour vous aider au
moment du dpannage et satisfaire votre curiosit sur le fonctionnement interne des classes de
collections avec paramtres.

Livre Java .book Page 796 Jeudi, 25. novembre 2004 3:04 15

796

Au cur de Java 2 - Notions fondamentales

Dfinition dune classe gnrique simple


Une classe gnrique est une classe comprenant une ou plusieurs variables de type. Dans ce chapitre, nous utilisons une classe Pair simple titre dexemple. Elle nous permet de nous concentrer sur
le gnrique, sans tre distraits par les dtails de stockage des donnes. Voici le code de la classe
Pair gnrique :
public class Pair<T>
{
public Pair() { first = null; second = null; }
public Pair(T first, T second) { this.first = first; this.
second = second; }
public T getFirst() { return first; }
public T getSecond() { return second; }
public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
private T first;
private T second;
}

La classe Pair introduit une variable de type T, incluse entre < > aprs le nom de la classe. Une
classe gnrique peut possder plusieurs variables de type. La classe Pair aurait pu, par exemple,
tre dfinie avec des types spars pour le premier et le deuxime champ :
public class Pair<T, U> { . . . }

Les variables de type seront utilises tout au long de la dfinition de la classe pour spcifier les types
de retour des mthodes et les types de champs et de variables locales. Par exemple :
private T first; // utilise une variable de type

INFO
Les variables de type sont souvent indiques avec des majuscules, ce qui permet de les garder courtes. La bibliothque Java utilise la variable E pour le type dlment dune collection, K et V pour les types de cls et de valeurs dune
table et T (et les lettres U et S, si ncessaire) pour "nimporte quel type".

Vous instanciez le type gnrique en remplaant les types pour les variables de type, comme
Pair<String>

On peut imaginer le rsultat comme une classe ordinaire avec les constructeurs
Pair<String>()
Pair<String>(String, String)

et les mthodes
String getFirst()
String getSecond()
void setFirst(String)
void setSecond(String)

En dautres termes, la classe gnrique agit la manire dun factory pour les classes ordinaires.
Le programme de lExemple 13.1 met en uvre la classe Pair. La mthode minmax statique
parcourt un tableau et calcule en mme temps la valeur minimale et la valeur maximale. Elle utilise
un objet Pair pour renvoyer les deux rsultats. Souvenez-vous que la mthode compareTo compare

Livre Java .book Page 797 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

797

deux chanes et renvoie 0 si les chanes sont identiques, un entier ngatif si la premire chane vient
avant la seconde dans lordre alphabtique et un entier positif dans les autres cas.
INFO C++
Au premier abord, les classes gnriques en Java sont identiques aux classes de modles en C++. La seule diffrence
vidente rside dans le fait que Java ne possde aucun mot cl template spcial. Toutefois, comme vous le verrez
dans ce chapitre, il existe des diffrences considrables entre ces deux mcanismes.

Exemple 13.1 : PairTest1.java


public class PairTest1
{
public static void main(String[] args)
{
String[] words = { "Mary", "had", "a", "little", "lamb" };
Pair<String> mm = ArrayAlg.minmax(words);
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
}
}
class ArrayAlg
{
/**
Rcupre le minimum et le maximum dun tableau de chanes.
@param a Un tableau de chane
@return Une paire avec la valeur min et la valeur max ou null si a est
nul ou vide
*/
public static Pair<String> minmax(String[] a)
{
if (a == null || a.length == 0) return null;
String min = a[0];
String max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.compareTo(a[i]) > 0) min = a[i];
if (max.compareTo(a[i]) < 0) max = a[i];
}
return new Pair<String>(min, max);
}
}

Mthodes gnriques
Dans la section prcdente, vous avez vu comment dfinir une classe gnrique. Vous pouvez aussi
dfinir une seule mthode avec des paramtres de type :
class ArrayAlg
{
public static <T> T getMiddle(T[] a)
{
return a[a.length / 2]);
}
}

Livre Java .book Page 798 Jeudi, 25. novembre 2004 3:04 15

798

Au cur de Java 2 - Notions fondamentales

Cette mthode est dfinie dans une classe ordinaire, et non dans une classe gnrique. Cest toutefois
une mthode gnrique, comme vous pouvez le voir aux signes qui lentourent et la variable de
type. Sachez que les variables de type sont insres aprs les modifieurs (public static, dans ce
cas) et avant le type de retour.
Vous pouvez dfinir des mthodes gnriques dans des classes ordinaires et dans des classes gnriques.
Lorsque vous appelez une mthode gnrique, vous pouvez placer les types rels, entours des
signes <>, avant le nom de la mthode :
String[] names = { "John", "Q.", "Public" };
String middle = ArrayAlg.<String>getMiddle(names);

Dans ce cas, et donc la plupart du temps, vous pouvez omettre le paramtre de type <String> de
lappel de mthode. Le compilateur dispose de suffisamment dinformations pour en dduire la
mthode que vous souhaitez utiliser. Il fait correspondre le type des names (donc, String[]) avec le
type gnrique T[] et en dduit que T doit tre un String. Vous pouvez donc simplement appeler
String middle = ArrayAlg.getMiddle(names);

INFO C++
En C++, les paramtres de type se placent aprs le nom de la mthode. Ceci peut amener des ambiguts danalyse
dsagrables. Par exemple, g(f<a,b>(c)) peut signifier "appeler g avec le rsultat de f<a,b>(c)" ou "appeler
g avec les deux valeurs boolennes f<a et b>(c)".

Limites pour variables de type


Par moments, une classe ou une mthode doit placer des restrictions sur des variables de type.
En voici un exemple ordinaire. Nous voulons calculer le plus petit lment dun tableau :
class ArrayAlg
{
public static <T> T min(T[] a) // presque correct
{
if (a == null || a.length == 0) return null;
T smallest = a[0];
for (int i = 1; i < a.length; i++)
if (smallest.compareTo(a[i]) > 0) smallest = a[i];
return smallest;
}
}

Mais un problme demeure. Etudiez le code de la mthode min. La variable smallest possde un
type T, ce qui signifie quil pourrait sagir dun objet dune classe arbitraire. Comment savoir alors
que la classe laquelle appartient T possde une mthode compareTo?
La solution consiste restreindre T une classe qui implmente linterface Comparable, une interface standard disposant dune seule mthode, compareTo. Pour y parvenir, vous devez donner une
limite pour la variable de type T:
public static <T extends Comparable> T min(T[] a) . . .

Livre Java .book Page 799 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

799

En fait, linterface Comparable est elle-mme un type gnrique. Pour lheure, nous allons ignorer
cette complexit.
Dsormais, la mthode gnrique min ne peut plus tre appele quavec des tableaux de classes qui
implmentent linterface Comparable, comme String, Date, etc. Appeler min avec un tableau
Rectangle produit une erreur de dlai de compilation car la classe Rectangle nimplmente pas
Comparable.
INFO C++
En C++, vous ne pouvez pas restreindre les types des paramtres de modles. Lorsquun programmeur instancie un
modle avec un type inadapt, un message derreur (souvent abscons) est signal dans le code du modle.

Vous vous demanderez peut-tre pourquoi utiliser le mot cl extends plutt que le mot cl implements
dans cette situation (aprs tout, Comparable est une interface). La notation
<T extends BoundingType>

indique que T doit tre un sous-type du type limitant. T et le type limitant peuvent tre une classe ou
une interface. Le mot cl extends a t choisi car il constitue une approximation raisonnable du
concept de sous-type et que les concepteurs Java ne souhaitaient pas ajouter un nouveau mot cl
(comme sub).
Une variable de type ou joker peut avoir plusieurs limites, par exemple
T extends Comparable & Serializable

Les types limitants sont spars par des esperluettes (&) car les virgules sont utilises pour sparer
les variables de type.
Comme pour lhritage Java, vous pouvez disposer dautant de supertypes dinterfaces que vous le
souhaitez, mais une seule des limites peut tre une classe. Si vous disposez dune classe agissant
comme lment limitant, elle doit figurer en premire place dans la liste.
Dans le prochain exemple de programme (voir Exemple 13.2), nous rcrivons la mthode minmax
de manire la rendre gnrique.
La mthode calcule le minimum et le maximum dun tableau gnrique et renvoie un Pair<T>.
Exemple 13.2 : PairTest2.java
import java.util.*;
public class PairTest2
{
public static void main(String[] args)
{
GregorianCalendar[] birthdays =
{
new GregorianCalendar(1906, Calendar.DECEMBER, 9), // G. Hopper
new GregorianCalendar(1815, Calendar.DECEMBER, 10),
// A. Lovelace
new GregorianCalendar(1903, Calendar.DECEMBER, 3),
// J. von Neumann
new GregorianCalendar(1910, Calendar.JUNE, 22), // K. Zuse
};

Livre Java .book Page 800 Jeudi, 25. novembre 2004 3:04 15

800

Au cur de Java 2 - Notions fondamentales

Pair<GregorianCalendar> mm = ArrayAlg.minmax(birthdays);
System.out.println("min = " + mm.getFirst().getTime());
System.out.println("max = " + mm.getSecond().getTime());
}
}
class ArrayAlg
{
/**
Rcupre le minimum et le maximum dun tableau dobjets de type T.
@param a Un tableau dobjets de type T
@return Une paire avec la valeur min et la valeur max,
ou null si a est nul ou vide
*/
public static <T extends Comparable> Pair<T> minmax(T[] a)
{
if (a == null || a.length == 0) return null;
T min = a[0];
T max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.compareTo(a[i]) > 0) min = a[i];
if (max.compareTo(a[i]) < 0) max = a[i];
}
return new Pair<T>(min, max);
}
}

Code gnrique et machine virtuelle


La machine virtuelle ne possde pas dobjet de type gnrique (tous les objets appartiennent des
classes ordinaires). Une prcdente version de limplmentation gnrique pouvait mme compiler
un programme utilisant le gnrique dans des fichiers de classe qui sexcutaient sur les machines
virtuelles 1.0 ! Cette compatibilit en amont na t abandonne que tard dans le dveloppement du
gnrique Java. Si vous utilisez le compilateur Sun pour compiler un code employant le gnrique
Java, les fichiers de classe qui en rsultent ne sexcuteront pas sur les machines virtuelles avant la
version 5.0.
INFO
Si vous souhaitez profiter des fonctionnalits du langage JDK 5.0 tout en conservant la compatibilit des bytecodes
avec danciennes machines virtuelles, consultez ladresse http://sourceforge.net/projects/retroweaver. Le
programme Retroweaver rcrit les fichiers de classe de sorte quils soient compatibles avec danciennes machines
virtuelles.

Ds que vous dfinissez un type gnrique, un type brut correspondant est automatiquement fourni.
Le nom du type brut correspond simplement au nom du type gnrique, les paramtres de type ayant
t supprims. Les variables de type sont effaces et remplaces par leurs types limitants (ou Object
pour les variables sans limites).

Livre Java .book Page 801 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

801

Par exemple, le type brut pour Pair<T> ressemble ceci :


public class Pair
{
public Pair(Object first, Object second)
{
this.first = first;
this.second = second;
}
public Object getFirst() { return first; }
public Object getSecond() { return second; }
public void setFirst(Object newValue) { first = newValue; }
public void setSecond(Object newValue) { second = newValue; }
private Object first;
private Object second;
}

T tant une variable sans limites, elle est simplement remplace par Object.
Il en rsulte une classe ordinaire, que vous pourriez avoir implmente avant lajout du gnrique au
langage de programmation Java.
Vos programmes peuvent contenir diffrentes sortes de Pair, comme Pair<String> ou
Pair<Gregorian-Calendar>, mais leffacement les transforme tous en types Pair bruts.
INFO C++
A cet gard, le gnrique Java est trs diffrent des modles C++. C++ produit diffrents types pour chaque instanciation de modle, un phnomne dnomm "gonflement du code du modle". Java ne souffre pas de ce problme.

Le type brut remplace les variables de type par la premire limite ou par Object si aucune limite
nest donne.
Par exemple, la variable de type de la classe Pair<T> ne dispose pas de limites explicites, le type
brut remplace donc T par Object. Supposons que nous dclarions un type lgrement diffrent :
public class Interval<T extends Comparable & Serializable>
implements Serializable
{
public Interval(T first, T second)
{
if (first.compareTo(second) <= 0) { lower = first; upper = second; }
else { lower = second; upper = first; }
}
. . .
private T lower;
private T upper;
}

Le type brut Interval ressemble ceci :


public class Interval implements Serializable
{
public Interval(Comparable first, Comparable second) { . . . }
. . .
private Comparable lower;
private Comparable upper;
}

Livre Java .book Page 802 Jeudi, 25. novembre 2004 3:04 15

802

Au cur de Java 2 - Notions fondamentales

INFO
Vous vous demandez peut-tre ce qui se passe si vous inversez les limites : class Interval<Serializable &
Comparable>. Dans ce cas, le type brut remplace T par Serializable et le compilateur insre des transtypages
dans Comparable le cas chant. Pour des raisons defficacit, vous devrez donc placer les interfaces de balisage (
savoir des interfaces sans mthodes) la fin de la liste des limites.

Traduire les expressions gnriques


Dans la programmation dun appel une mthode gnrique, le compilateur insre des transtypages
au moment o le type de retour est effac. Considrons par exemple la suite dinstructions
Pair<Employee> buddies = . . .;
Employee buddy = buddies.getFirst();

Effacer getFirst renvoie le type Object. Le compilateur insre automatiquement le transtypage sur
Employee. En fait, il traduit lappel de mthode en deux instructions de machine virtuelle :
m

un appel la mthode brute Pair.getFirst;

un transtypage de lobjet renvoy au type Employee.

Des transtypages sont galement insrs lorsque vous accdez un champ gnrique. Supposons
que les premier et deuxime champs de la classe Pair soient publics (ce nest peut-tre pas un bon
style de programmation, mais cest autoris dans Java).
Ds lors, dans lexpression
Employee buddy = buddies.first;

un transtypage a t insr dans les bytecodes qui en rsultent.

Traduire les mthodes gnriques


Leffacement de type survient galement pour les mthodes gnriques. Les programmeurs imaginent
gnralement une mthode gnrique comme
public static <T extends Comparable> T min(T[] a)

comme une famille de mthodes globales ; or, aprs leffacement, une seule mthode demeure :
public static Comparable min(Comparable[] a)

Sachez que le paramtre de type T a t effac, ne laissant que son type limite Comparable.
Leffacement de la mthode met au jour deux difficults. Etudiez cet exemple :
class DateInterval extends Pair<Date>
{
public void setSecond(Date second)
{
if (second.compareTo(getFirst()) >= 0)
super.setSecond(second);
}
. . .
}

Livre Java .book Page 803 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

803

Un intervalle de date est une paire dobjets Date; nous voulons craser les mthodes pour nous
assurer que la deuxime valeur ne soit jamais infrieure la premire. Cette classe est efface :
class DateInterval extends Pair // aprs effacement
{
public void setSecond(Date second) { . . . }
. . .
}

Ceci peut surprendre, mais il existe une autre mthode setSecond, hrite de Pair, savoir
public void setSecond(Object second)

Cest lvidence une mthode diffrente car elle possde un paramtre dun type diffrent, qui est
Object et non Date. Or elle ne doit pas tre diffrente. Regardez cette suite dinstructions :
DateInterval interval = new DateInterval(. . .);
Pair<Date> pair = interval; // OK--attribution la superclasse
pair.setSecond(aDate);

On peut sattendre ce que lappel setSecond soit polymorphique et que la mthode approprie
soit appele. Etant donn que pair fait rfrence un objet DateInterval, il doit sagir de DateInterval.setSecond. Le problme est que leffacement du type interfre avec le polymorphisme.
Pour le rsoudre, le compilateur gnre une mthode bridge dans la classe DateInterval:
public void setSecond(Object second) { setSecond((Date) second); }

Pour connatre les raisons de cette russite, suivons attentivement lexcution de linstruction
pair.setSecond(aDate)

La variable pair a dclar le type Pair<Date> et ce type na quune seule mthode appele
setSecond, savoir setSecond(Object). La machine virtuelle appelle cette mthode sur lobjet
auquel pair fait rfrence. Cet objet est de type DateInterval. Donc, la mthode DateInterval.setSecond(Object) est appele. Cest la mthode bridge synthtise. Elle appelle DateInterval.setSecond(Date), et cest bien ce que nous voulons.
Les mthodes bridge peuvent apparatre encore plus tranges. Supposons que la mthode DateInterval crase aussi la mthode getSecond:
class DateInterval extends Pair<Date>
{
public Date getSecond() { return (Date) super.getSecond().clone(); }
. . .
}

Dans le type effac, il y a deux mthodes getSecond:


Date getSecond() // dfini dans DateInterval
Object getSecond() // dfini dans Pair

Ce code Java est impossible, deux mthodes ne peuvent pas avoir les mmes types de paramtres,
cest--dire ici aucun. Toutefois, dans la machine virtuelle, les types de paramtres et le type de
retour spcifient une mthode. Le compilateur peut donc produire des bytecodes pour deux mthodes
qui ne diffrent quau niveau de leur type de retour ; la machine virtuelle grera correctement cette
situation.

Livre Java .book Page 804 Jeudi, 25. novembre 2004 3:04 15

804

Au cur de Java 2 - Notions fondamentales

INFO
Les mthodes bridge ne sont pas restreintes aux types gnriques. Nous avons dj not au Chapitre 5 que, depuis
le JDK 5.0, une mthode peut spcifier un type de retour plus restrictif lors de lcrasement dune autre mthode. Par
exemple :
public class Employee implements Cloneable
{
public Employee clone() throws CloneNotSupportedException { ... }
}

Les mthodes Object.clone et Employee.clone sont dites avoir des types de retour covariants.
En fait, la classe Employee possde deux mthodes clone:
Employee clone() // dfini ci-dessus
Object clone() // mthode bridge synthtise, crase Object.clone

La mthode bridge synthtise appelle la mthode nouvellement dfinie.

En rsum, notez bien ces points lis la traduction du gnrique Java :


m

Il nexiste pas de gnrique dans les machines virtuelles, uniquement des classes et des mthodes
ordinaires.

Tous les types de paramtres sont remplacs par leurs limites.

Les mthodes bridge sont synthtises pour prserver le polymorphisme.

Les transtypages sont insrs en fonction des besoins pour prserver la scurit du type.

Appeler un code existant


De grandes quantits de code Java ont t crites avant le JDK 5.0. Si les classes gnriques ne
pouvaient pas interagir avec ce code, elles ne seraient probablement pas frquemment utilises.
Heureusement, lutilisation des classes gnriques avec leurs quivalents bruts se fait en toute
simplicit dans les API existantes.
Etudions un exemple concret. Pour dfinir les tiquettes dun JSlider, vous utilisez la mthode
void setLabelTable(Dictionary table)

Au Chapitre 9, nous avons utilis le code suivant pour remplir la table des tiquettes :
Dictionary<Integer, Component> labelTable = new Hashtable<Integer, Component>
();
labelTable.put(0, new JLabel(new ImageIcon("nine.gif")));
labelTable.put(20, new JLabel(new ImageIcon("ten.gif")));
. . .
slider.setLabelTable(labelTable); // ATTENTION

INFO
La classe Hashtable est une sous-classe concrte de la classe abstraite Dictionary. Dictionary et Hashtable
ont toutes deux t dclares "obsoltes" depuis quelles ont t supplantes par linterface Map et la classe HashMap
du JDK 1.2. En apparence, cependant, elles existent toujours et continuent svir.

Livre Java .book Page 805 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

805

Aprs tout, la classe JSlider na t ajoute qu partir du JDK 1.3. Ses programmeurs ne connaissaient-ils pas la
classe Map? Pensez-vous que cela leur fera adopter le gnrique dans un proche avenir ? Voil ce quil en est du
code existant.

Dans le JDK 5.0, les classes Dictionary et Hashtable ont t transformes en classes gnriques.
Nous pouvons donc former un Dictionary<Integer, Component> au lieu dutiliser un Dictionary brut. Toutefois, lorsque vous passez lobjet Dictionary<Integer, Component> setLabelTable, le compilateur met un avertissement :
Dictionary<Integer, Components> labelTable = . . .;
slider.setLabelTable(labelTable); // ATTENTION

Aprs tout, le compilateur na aucune assurance des effets de setLabelTable sur lobjet Dictionary. Cette mthode pourrait remplacer toutes les cls par des chanes, ce qui annulerait la garantie
que les cls ont le type Integer. Les oprations venir pourraient alors lancer de mauvaises exceptions
de transtypage.
Vous ne pouvez pas faire grand-chose avec cet avertissement, part ltudier et demander ce que
JSlider risque de faire avec cet objet Dictionary. Ici, il est assez vident que JSlider ne fait que
lire les informations, nous pouvons donc ignorer lavertissement.
Envisageons maintenant le cas contraire : vous obtenez un objet dun type brut depuis une classe
existante. Vous pouvez lassigner une variable de type avec paramtres, mais vous obtiendrez bien
sr un avertissement.
Par exemple :
Dictionary<Integer, Components> labelTable = slider.getLabelTable();
// ATTENTION

Trs bien, revoyons lavertissement et assurons-nous que la table des tiquettes contient rellement
les objets Integer et Component. La garantie nest bien entendu jamais absolue. Un codeur
malveillant pourrait avoir install un Dictionary diffrent dans le slider. Mais, une fois de plus, la
situation nest pas pire quavant le JDK 5.0. Dans le pire des cas, votre programme renverra une
exception.
Il est regretter quon ne puisse pas dsactiver les avertissements aprs les avoir consults. Il est
assez ennuyeux de revoir chaque avertissement chaque fois que vous recompilez un fichier source.
Les concepteurs du langage Java prvoient dajouter un mcanisme davertissement plus flexible
dans une version future du JDK.

Restrictions et limites
Dans les sections venir, nous nous attarderons sur plusieurs restrictions connatre lorsque vous
travaillez avec le gnrique Java. La plupart de ces restrictions sont une consquence de leffacement
des types.

Types primitifs
Il est impossible de remplacer un type primitif par un paramtre de type. Il nexiste donc pas de
Pair<double>, uniquement Pair<Double>. Ceci est d, bien entendu, leffacement du type.

Livre Java .book Page 806 Jeudi, 25. novembre 2004 3:04 15

806

Au cur de Java 2 - Notions fondamentales

Aprs leffacement, la classe Pair possde des champs du type Object, et vous ne pouvez pas les
utiliser pour stocker des valeurs double.
Ceci est fort ennuyeux, mais cohrent avec le statut distinct des types primitifs dans le langage Java.
Ce nest toutefois pas une limitation terrible, il nexiste que huit types primitifs, et vous pouvez
toujours les grer avec des classes et des mthodes spares lorsque les types denveloppe ne constituent
pas un remplacement acceptable.

Informations sur le type dexcution


Les objets de la machine virtuelle ont toujours un type non gnrique spcifique. Ainsi, toutes les
demandes de renseignements sur le type ne produisent que du type brut. Par exemple,
if (a instanceof Pair<String>) // identique un instanceof Pair

ne teste rellement que si a est un Pair de quelque type que ce soit. Ceci vaut galement pour le test
if (a instanceof Pair<T>) // T est ignor

ou le transtypage
Pair<String> p = (Pair<String>) a; // ATTENTION--ne peut tester
que si a est un Pair

Pour vous le rappeler, vous obtiendrez un avertissement de compilateur ds que vous utilisez
instanceof ou des expressions de transtypage qui impliquent des types gnriques.
Dans le mme esprit, la mthode getClass renvoie toujours le type brut. Par exemple :
Pair<String> stringPair = . . .;
Pair<Employee> employeePair = . . .;
if (stringPair.getClass() == employeePair.getClass()) // ils sont gaux

La comparaison se vrifie car les deux appels getClass renvoient Pair.class.

Exceptions
Vous ne pouvez ni dclencher ni intercepter des objets dune classe gnrique. En fait, une classe
gnrique na mme pas le droit dtendre Throwable. Par exemple, la dfinition suivante ne pourra
pas tre compile :
public class Problem<T> extends Exception { /* . . . */ }
// ERREUR--impossible dtendre Throwable

Vous ne pouvez pas non plus utiliser une variable de type dans une clause catch. Par exemple, la
mthode suivante ne pourra pas tre compile :
public static <T extends Throwable> void doWork(Class<T> t)
{
try
{
fonctionne
}
catch (T e) // ERREUR--impossible dintercepter la variable de type
{
Logger.global.info(...)
}
}

Livre Java .book Page 807 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

807

Vous pouvez toutefois utiliser des variables de type dans les spcifications dexceptions. La mthode
suivante est autorise :
public static <T extends Throwable> void doWork(T t) throws T // OK
{
try
{
fonctionne
}
catch (Throwable realCause)
{
t.initCause(realCause);
throw t;
}
}

Tableaux
Il est impossible de dclarer des tableaux de types avec paramtres comme
Pair<String>[] table = new Pair<String>(10); // ERREUR

En effet, aprs leffacement, le type de table est Pair[]. Vous pouvez le transformer en Object[]:
Object[] objarray = table;

Nous avons vu au Chapitre 5 quun tableau se souvient de son type de composant et quil dclenche
une exception ArrayStoreException si vous essayez de stocker un lment dun type erron :
objarray[0] = "Hello"; // ERREUR--le type de composant est Pair

Mais leffacement rend ce mcanisme inefficace pour les types gnriques. Lattribution
objarray[0] = new Pair<Employee>();

passerait la vrification de stockage du tableau mais entrane toujours une erreur de type. Pour cette
raison, les tableaux de types avec paramtres sont interdits.
ASTUCE
Si vous devez collecter des objets de type avec paramtres, utilisez simplement un ArrayList:

ArrayList<Pair<String>> fonctionne de manire sre et efficace.

Instanciation de types gnriques


Vous ne pouvez pas instancier les types gnriques. Par exemple, le constructeur Pair<T> suivant est
interdit :
public Pair() { first = new T(); second = new T(); } // ERREUR

Leffacement du type transformerait T en Object, et vous ne voulez certainement pas appeler un


nouvel Object().
De mme, vous ne pouvez pas raliser un tableau gnrique :
public <T> T[] minMax(T[] a) { T[] mm = new T[2]; . . . } // ERREUR

Leffacement du type amnerait cette mthode construire un tableau Object[2] chaque fois.

Livre Java .book Page 808 Jeudi, 25. novembre 2004 3:04 15

808

Au cur de Java 2 - Notions fondamentales

Toutefois, vous pouvez construire des objets et des tableaux gnriques grce la rflexion, en appelant
les mthodes Class.newInstance et Array.newInstance.

Contextes statiques
Il est impossible de rfrencer les variables de type dans des champs ou des mthodes statiques.
Par exemple, cette brillante ide ne fonctionnerait pas :
public class Singleton<T>
{
public static T getSingleInstance() // ERREUR
{
if (singleInstance == null) construct new instance of T
return singleInstance;
}
private static T singleInstance; // ERREUR
}

Si ctait possible, un programme pourrait dclarer un Singleton<Random> pour partager un gnrateur de nombres alatoires et un Singleton<JFileChooser> pour partager une bote de dialogue
de slection de fichiers. Mais cela ne fonctionnera pas. Aprs leffacement du type, il ne reste quune
classe Singleton et un seul champ singleInstance.
Pour cette raison, les champs et les mthodes statiques ayant des variables de type sont purement
interdits.

Conflits aprs un effacement


Les conditions entranant des conflits lors de leffacement des types gnriques sont interdites. En
voici un exemple. Supposons que nous ajoutions une mthode equals la classe Pair, comme ceci :
public class Pair<T>
{
public boolean equals(T value) { return first.equals(value) &&
second.equals(value); }
. . .
}

Envisagez un Pair<String>. De par son concept, il possde deux mthodes equals:


boolean equals(String) // dfini dans Pair<T>
boolean equals(Object) // hrit de Object

Mais lintuition va nous garer. Leffacement de la mthode


boolean equals(T)

est
boolean equals(Object)

ce qui entre en conflit avec la mthode Object.equals.


La solution consiste, bien entendu, renommer la mthode lorigine du conflit.
La spcification du gnrique mentionne une autre rgle : "Pour prendre en charge la traduction par
effacement, une restriction est impose : une classe ou une variable de type ne peut pas, dans le

Livre Java .book Page 809 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

809

mme temps, tre un sous-type de deux types dinterface qui constituent des paramtrages diffrents
de la mme interface." Par exemple, le code suivant est interdit :
class Calendar implements Comparable<Calendar> { . . . }
class GregorianCalendar extends Calendar implements
Comparable<GregorianCalendar> { . . . } // ERREUR

GregorianCalendar implmenterait alors Comparable<Calendar> et Comparable<GregorianCalendar>, des paramtrages diffrents de la mme interface.
Le rapport de cette restriction avec leffacement de type nest pas vident. La version non gnrique
class Employee implements Comparable { . . . }
class Manager extends Employee implements Comparable { . . . }

est autorise.

Rgles dhritage pour les types gnriques


Lorsque vous travaillez avec les classes gnriques, vous devez apprendre quelques rgles sur lhritage et les sous-types. Commenons par une situation que de nombreux programmeurs ne jugent pas
intuitive. Envisagez une classe et une sous-classe comme Employee et Manager. Pair<Manager>
est-il une sous-classe de Pair<Employee>?
Bizarrement, la rponse est "non". Par exemple, le code suivant ne sera pas compil :
Manager[] topHonchos = . . .;
Pair<Employee> = ArrayAlg.minmax(topHonchos); // ERREUR

La mthode minmax renvoie Pair<Manager>, et non Pair<Employee>, et vous ne pouvez pas affecter
lune lautre.
En gnral, il nexiste pas de relation entre Pair<S> et Pair<T>, quels que soient les lments
auxquels S et T sont relis (voir Figure 13.1).
Cette restriction peut paratre cruelle, mais elle est ncessaire la scurit des types. Supposons que
nous soyons autoriss transformer un Pair<Manager> en Pair<Employee>. Etudiez ce code :
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair<Employee> employeeBuddies = managerBuddies;
// interdit, mais supposons que ce ne le soit pas
employeeBuddies.setFirst(lowlyEmployee);

Figure 13.1
Aucune relation
d'hritage entre
les classes de paires.

Employee

Pair
<Employee>

Pas de relation !

Manager

Pair
<Manager>

Livre Java .book Page 810 Jeudi, 25. novembre 2004 3:04 15

810

Au cur de Java 2 - Notions fondamentales

A lvidence, la dernire dclaration est autorise. Mais employeeBuddies et managerBuddies font


rfrence au mme objet. Nous avons donc russi apparier le directeur financier avec un simple
employ, ce qui ne devrait pas tre possible pour un Pair<Manager>.
Au contraire, vous pouvez toujours transformer un type avec paramtres en type brut. Par exemple,
Pair<Employee> est un sous-type du type brut Pair. Cette conversion est ncessaire pour linterfaage avec le code existant.
Est-il possible de procder une conversion en type brut, puis de crer une erreur de type ? Malheureusement, oui. Etudiez cet exemple :
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair rawBuddies = managerBuddies; // OK
rawBuddies.setFirst(new File(". . .")); // avertissement de temps
de compilation uniquement

Ceci est assez effrayant. Sachez toutefois que ce nest pas pire quavec les anciennes versions de
Java. La scurit de la machine virtuelle nest pas remise en cause. Lorsque lobjet tranger est rcupr avec getFirst et attribu une variable Manager, une exception ClassCastException est
dclenche, comme auparavant. Vous perdez juste le surcrot de scurit apport gnralement par la
programmation gnrique.
Enfin, les classes gnriques peuvent tendre ou implmenter dautres classes gnriques. A cet
gard, elles ne sont pas diffrentes des classes ordinaires. Par exemple, la classe ArrayList<T>
implmente linterface List<T>. Cela signifie quun ArrayList<Manager> peut tre converti
en List<Manager>. Toutefois, comme vous venez de le voir, un ArrayList<Manager> nest
pas un ArrayList<Employee> ni un List<Employee>. La Figure 13.2 prcise ces relations.

Types joker
Tous ceux qui ont effectu quelques recherches avec les systmes de type savent depuis quelque
temps dj quun systme rigide nest pas trs agrable utiliser. Les concepteurs Java ont
invent une "sortie de secours" ingnieuse (tout en tant sre) : le type joker. Par exemple, le
type joker
Pair<? extends Employee>

remplace toute paire gnrique dont le paramtre de type est une sous-classe de Employee, comme
Pair<Manager>, mais pas Pair<String>.
Imaginons que vous vouliez crire une mthode qui affiche des paires demploys, comme ceci :
public static void printBuddies(Pair<Employee> p)
{
Employee first = p.getFirst();
Employee second = p.getSecond();
System.out.println(first.getName() + "
and " + second.getName() + " are buddies.";
}

Livre Java .book Page 811 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

811

Figure 13.2
<< interface >>

Relations de sous-type
parmi les types de liste
gnrique.

List
(raw)

<< interface >>

<< interface >>

List
<Manager>

List
<Employee>

ArrayList
(raw)

ArrayList
<Manager>

ArrayList
<Employee>

Pas de relation !

Comme vous lavez vu dans la section prcdente, vous ne pouvez pas passer un Pair<Manager>
cette mthode. Voil qui est plutt limitatif. La solution est simple, utilisez un type joker :
public static void printBuddies(Pair<? extends Employee> p)

Le type Pair<Manager> est un sous-type de Pair<? extends Employee> (voir Figure 13.3).
Pouvons-nous utiliser les jokers pour corrompre un Pair<Manager> via une rfrence Pair<?
extends Employee>?
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair<? extends Employee> wildcardBuddies = managerBuddies; // OK
wildcardBuddies.setFirst(lowlyEmployee); // Erreur de dlai de compilation

Aucune corruption nest possible. Lappel setFirst est une erreur de type. Pour en connatre les
raisons, tudions plus avant le type Pair<? extends Employee>. Ses mthodes ressemblent ceci :
? extends Employee getFirst()
void setFirst(? extends Employee)

Avec cela, impossible dappeler la mthode setFirst. Tout ce que sait le compilateur, cest quil a
besoin dun sous-type de Employee, mais il ne sait pas de quel type. Il refuse de passer les types
spcifiques. Aprs tout, ? pourrait ne pas convenir.

Livre Java .book Page 812 Jeudi, 25. novembre 2004 3:04 15

812

Au cur de Java 2 - Notions fondamentales

Nous ne rencontrons pas ce problme avec getFirst: vous pouvez tout fait attribuer la valeur de
retour de getFirst une rfrence Employee.
Cest lintrt principal des jokers limits. Nous disposons maintenant dune mthode pour faire la
distinction entre les mthodes daccs sres et les mthodes de modification peu sres.
Figure 13.3
Relations de sous-type
avec les jokers.

Pair
(raw)

Pair
<? extends
Employee>

Pair
<Manager>

Pair
<Employee>

Limites de supertypes pour les jokers


Les limites de jokers sont identiques aux limites de variables de type, mais elles disposent dune
fonctionnalit supplmentaire : vous pouvez spcifier une limite de supertype, comme ceci :
? super Manager

Ce joker est limit aux supertypes de Manager (un coup de chance que le mot cl super dcrive la
relation aussi prcisment).
Quelles sont les raisons de cette opration ? Un joker ayant une limite de supertype a le comportement oppos celui des jokers dcrits. Vous pouvez fournir des paramtres aux mthodes mais non
utiliser les valeurs de retour. Par exemple, Pair<? super Manager> possde les mthodes
void set(? super Manager)
? super Manager get()

Le compilateur ne connat pas le type exact de la mthode set, il peut lappeler avec tout objet
Manager (ou un sous-type comme Executive), mais pas avec un Employee. Et, si vous appelez get,
vous nobtenez aucune garantie sur le type de lobjet renvoy. Vous ne pouvez lattribuer qu un
Object.
Voici un exemple assez commun. Nous avons un tableau de directeurs et voulons placer le directeur
ayant la prime la plus petite et celui ayant la plus grosse prime dans un objet Pair. Un
Pair<Employee> devrait faire laffaire ou, peut-tre, un Pair<Object> (voir Figure 13.4). La mthode
suivante acceptera tout Pair adapt :
public static void minMaxBonus(Manager[] a, Pair<? super Manager> result)
{

Livre Java .book Page 813 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

813

if (a == null || a.length == 0) return;


Manager min = a[0];
Manager max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.getBonus() > a[i].getBonus()) min = a[i];
if (max.getBonus() < a[i].getBonus()) max = a[i];
}
result.setFirst(min);
result.setSecond(max);
}

Figure 13.4
Un joker avec une limite
de supertype.

Pair
(raw)

Pair<?>

Pair
<? super
Manager>

Pair
<Employee>

Pair
<Object>

Logiquement, les jokers ayant des limites de supertype vous permettent dcrire vers un objet gnrique et ceux ayant des limites de sous-type, de lire partir dobjets gnriques.
Voici une autre utilisation des limites de supertype. Linterface Comparable est, en elle-mme, un
type gnrique. Elle est dclare comme suit :
public interface Comparable<T>
{
public int compareTo(T other);
}

Ici, la variable de type prcise le type du paramtre other. Par exemple, la classe String implmente Comparable<String>, et sa mthode compareTo est dclare de la manire suivante :
public int compareTo(String other)

Trs bien, le paramtre explicite possde le type correct. Avant le JDK 5.0, other tait un Object, et
il fallait insrer un transtypage dans limplmentation de la mthode.

Livre Java .book Page 814 Jeudi, 25. novembre 2004 3:04 15

814

Au cur de Java 2 - Notions fondamentales

Comparable tant un type gnrique, nous aurions peut-tre mieux fait dutiliser la mthode min de
la classe ArrayAlg. Nous aurions pu la dclarer ainsi :
public static <T extends Comparable< T>> T min(T[] a) . . .

Ceci vaut peut-tre mieux que de simplement utiliser T extends Comparable, et cela conviendrait
bien de nombreuses classes. Par exemple, si vous calculez le minimum dun tableau String, T est
le type String et String est un sous-type de Comparable<String>. Mais nous rencontrons un
problme au moment de traiter un tableau dobjets GregorianCalendar. GregorianCalendar est
en effet une sous-classe de Calendar et Calendar implmente Comparable<Calendar>. Ainsi,
GregorianCalendar implmente Comparable<Calendar> mais pas Comparable<GregorianCalendar>.
Dans une situation comme celle-ci, les supertypes viennent la rescousse :
public static <T extends Comparable<? super T>> T min(T[] a) . . .

Maintenant, la mthode compareTo a la forme


int compareTo(? super T)

Elle est peut-tre dclare pour accepter un objet de type T ou, par exemple lorsque T est GregorianCalendar, un supertype de T. Dans tous les cas, le passage dun objet de type T la mthode
compareTo se fera en toute scurit.
Pour les non-initis, une dclaration du type <T extends Comparable<? super T>> peut paratre
intimidante. Et cest dommage car son intention est daider les programmeurs dapplications
supprimer les restrictions inutiles sur les paramtres dappel. Ceux qui ne sont pas intresss par le
gnrique apprendront srement rapidement passer sur ces dclarations et prendront pour acquis
que les programmeurs de bibliothques ont fait le bon choix. Quant aux programmeurs de bibliothques, ils devront shabituer aux jokers, faute de quoi les utilisateurs les maudiront et lanceront des
transtypages alatoires sur le code jusqu ce quil se compile.

Jokers sans limites


Il est aussi possible dutiliser des jokers sans aucune limite, comme Pair<?>. Au premier abord, cela
ressemble au type brut Pair, mais ils sont en fait trs diffrents. Le type Pair<?> possde des
mthodes comme
? getFirst()
void setFirst(?)

La valeur de retour de getFirst ne peut tre attribue qu un Object. La mthode setFirst ne


peut jamais tre appele, mme pas avec un Object. Cest l la diffrence essentielle entre Pair<?>
et Pair: vous pouvez appeler la mthode setObject de la classe brute Pair avec nimporte quel
Object.
Mais pourquoi vouloir un type aussi timide ? Peut-tre parce quil convient pour des oprations trs
simples. La mthode suivante, par exemple, teste si une paire contient un objet donn. Elle na
jamais besoin du type rel :
public static boolean hasNulls(Pair<?> p)
{
return p.getFirst() == null || p.getSecond() == null;
}

Livre Java .book Page 815 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

815

Vous auriez pu viter le type joker en transformant contains en une mthode gnrique :
public static <T> boolean hasNulls(Pair<T> p)

Toutefois, la version contenant le type joker semble plus simple lire.

Capture de caractres joker


Ecrivons une mthode qui intervertit les lments dune paire :
public static void swap(Pair<?> p)

Un joker nest pas une variable de type, nous ne pouvons donc pas crire un code qui utilise ?
comme type. Autrement dit, le code suivant serait interdit :
? t = p.getFirst(); // ERREUR
p.setFirst(p.getSecond());
p.setSecond(t);

Ceci pose problme car nous devons conserver temporairement le premier lment lorsque nous
ralisons lchange. Heureusement, il existe une solution intressante ce problme. Nous pouvons
crire une mthode dassistance, swapHelper, comme suit :
public static <T> void swapHelper(Pair<T> p)
{
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}

Remarquez que swapHelper est une mthode gnrique ; au contraire de swap, elle a un paramtre
fixe de type Pair<?>.
Nous pouvons maintenant appeler swapHelper partir de swap:
public static void swap(Pair<?> p) { swapHelper(p); }

Dans ce cas, le paramtre T de la mthode swapHelper capture le joker. On ne connat pas le type
induit par le joker, mais cest un type dfini et la dfinition de <T>swapHelper est tout fait logique
lorsque T induit ce type.
Bien entendu, dans ce cas, nous navons pas t amens utiliser un joker, il ny a rien de mal utiliser un paramtre de type, comme dans la mthode swapHelper. Etudiez toutefois cet exemple, dans
lequel un type joker apparat naturellement au milieu dun calcul :
public static void maxMinBonus(Manager[] a, Pair<? super Manager> result)
{
minMaxBonus(a, result);
PairAlg.swapHelper(result); // OK--swapHelper capture le type joker
}

Ici, le mcanisme de capture du joker est invitable.


Cette capture nest autorise que dans des cas trs restreints. Le compilateur doit pouvoir garantir
que le joker reprsente un seul type dfini. Par exemple, le T dans ArrayList<Pair<T>> ne peut
jamais capturer le joker de ArrayList<Pair<?>>. La liste de tableau pourrait en effet contenir deux
Pair<?>, chacun ayant un type diffrent pour ?.
Le programme de test de lExemple 13.3 rassemble les diverses mthodes que nous avons vues dans
les sections prcdentes, elles sont ici replaces dans leur contexte.

Livre Java .book Page 816 Jeudi, 25. novembre 2004 3:04 15

816

Au cur de Java 2 - Notions fondamentales

Exemple 13.3 : PairTest3.java


import java.util.*;
public class PairTest3
{
public static void main(String[] args)
{
Manager ceo = new Manager("Gus Greedy", 800000, 2003, 12, 15);
Manager cfo = new Manager("Sid Sneaky", 600000, 2003, 12, 15);
Pair<Manager> buddies = new Pair<Manager>(ceo, cfo);
printBuddies(buddies);
ceo.setBonus(1000000);
cfo.setBonus(500000);
Manager[] managers = { ceo, cfo };
Pair<Employee> result = new Pair<Employee>();
minMaxBonus(managers, result);
System.out.println("first: " + result.getFirst().getName()
+ ", second: " + result.getSecond().getName());
maxMinBonus(managers, result);
System.out.println("first: " + result.getFirst().getName()
+ ", second: " + result.getSecond().getName());
}
public static void printBuddies(Pair<? extends Employee> p)
{
Employee first = p.getFirst();
Employee second = p.getSecond();
System.out.println(first.getName() + " and " + second.
getName() + " are buddies.");
}
public static void minMaxBonus(Manager[] a, Pair<? super Manager> result)
{
if (a == null || a.length == 0) return;
Manager min = a[0];
Manager max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.getBonus() > a[i].getBonus()) min = a[i];
if (max.getBonus() < a[i].getBonus()) max = a[i];
}
result.setFirst(min);
result.setSecond(max);
}
public static void maxMinBonus(Manager[] a, Pair<? super Manager> result)
{
minMaxBonus(a, result);
PairAlg.swapHelper(result); // OK--swapHelper capture le type joker
}
}

Livre Java .book Page 817 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

class PairAlg
{
public static boolean hasNulls(Pair<?> p)
{
return p.getFirst() == null || p.getSecond() == null;
}
public static void swap(Pair<?> p) { swapHelper(p); }
public static <T> void swapHelper(Pair<T> p)
{
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
}
class Employee
{
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(
year, month - 1, day);
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
private String name;
private double salary;
private Date hireDay;
}
class Manager extends Employee
{
/**
@param n Le nom de lemploy
@param s Le salaire

817

Livre Java .book Page 818 Jeudi, 25. novembre 2004 3:04 15

818

Au cur de Java 2 - Notions fondamentales

@param year Lanne dembauche


@param month Le mois dembauche
@param day Le jour dembauche
*/
public Manager(String n, double s, int year, int month, int day)
{
super(n, s, year, month, day);
bonus = 0;
}
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public void setBonus(double b)
{
bonus = b;
}
public double getBonus()
{
return bonus;
}
private double bonus;
}

Rflexion et gnrique
La classe Class est maintenant gnrique. A titre dexemple, String.class est en fait un objet
(lunique objet) de la classe Class<String>.
Le paramtre de type est utile car il permet aux mthodes de Class<T> dtre plus spcifiques sur
leur type de retour. Les mthodes suivantes de Class<T> profitent du paramtre de type :
T newInstance()
T cast(Object obj)
T[] getEnumConstants()
Class<? super T> getSuperclass()
Constructor<T> getConstructor(Class... parameterTypes)
Constructor<T> getDeclaredConstructor(Class... parameterTypes)

La mthode newInstance renvoie une instance de la classe, obtenue partir du constructeur par
dfaut. Son type de retour peut maintenant tre dclar comme tant T, le mme que celui de la
classe dcrite par Class<T>. Ceci pargne un transtypage.
La mthode cast renvoie lobjet donn, maintenant dclar comme type T si son type est en fait un
sous-type de T. Sinon il dclenche une exception BadCastException.
La mthode getEnumConstants renvoie null si cette classe nest pas une classe enum ou un tableau
des valeurs dnumration, connues comme tant du type T.

Livre Java .book Page 819 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

819

Enfin, les mthodes getConstructor et getDeclaredConstructor renvoient un objet Constructor<T>. La classe Constructor a aussi t rendue gnrique de sorte que sa mthode newInstance
possde le type de retour correct.
java.lang.Class<T> 1.0

T newInstance() 5.0

Renvoie une nouvelle instance construite avec le constructeur par dfaut.

T cast(Object obj) 5.0

Renvoie obj sil est null ou peut tre converti en type T, ou dclenche une exception BadCastException dans le cas contraire.

T[] getEnumConstants() 5.0

Renvoie un tableau de toutes les valeurs si T est un type numr, null autrement.

Class<? super T> getSuperclass() 5.0

Renvoie la superclasse de cette classe ou null si T nest pas une classe ou la classe Object.

Constructor<T> getConstructor(Class... parameterTypes) 5.0


Constructor<T> getDeclaredConstructor(Class... parameterTypes) 5.0

Rcuprent le constructeur public ou le constructeur ayant les types de paramtres donns.


java.lang.reflect.Constructor<T> 1.1

T newInstance(Object... parameters) 5.0

Renvoie une nouvelle instance construite avec les paramtres donns.

Utilisation des paramtres Class<T> pour la concordance de type


Il est parfois utile de faire concorder la variable de type dun paramtre Class<T> dans une mthode
gnrique. Voici lexemple canonique :
public static <T> Pair<T> makePair(Class<T> c) throws
InstantiationException, IllegalAccessException
{
return new Pair<T>(c.newInstance(), c.newInstance());
}

Si vous appelez
makePair(Employee.class)

Employee.class est un objet du type Class<Employee>. Le paramtre de type T de la mthode


makePair concorde avec Employee, et le compilateur peut en dduire que la mthode renvoie un
Pair<Employee>.

Informations de type gnrique dans la machine virtuelle


Lune des fonctionnalits remarquables du gnrique Java est leffacement des types gnriques dans
la machine virtuelle. Bizarrement, les classes effaces conservent une vague ide de leur origine gnrique. Par exemple, la classe brute Pair sait quelle est originaire de la classe gnrique Pair<T>,
mme si un objet de type Pair ne peut pas dire sil a t construit sous forme de Pair<String> ou de
Pair<Employee>.

Livre Java .book Page 820 Jeudi, 25. novembre 2004 3:04 15

820

Au cur de Java 2 - Notions fondamentales

De mme, envisagez une mthode


public static Comparable min(Comparable[] a)

qui est leffacement dune mthode gnrique


public static <T extends Comparable<? super T>> T min(T[] a)

Vous pouvez utiliser les amliorations de lAPI rflexion du JDK 5.0 pour dterminer si :
m

La mthode gnrique dispose dun paramtre de type appel T.

Le paramtre de type possde une limite de sous-type qui est elle-mme un type gnrique.

Le type de limite possde un paramtre joker.

Le paramtre joker possde une limite de supertype.

La mthode gnrique possde un paramtre de tableau gnrique.

En dautres termes, vous devez tout reconstruire sur les classes et les mthodes gnriques que leurs
implmenteurs ont dclares. Toutefois, vous ne saurez pas comment les paramtres de type ont t
rsolus pour des appels dobjets ou de mthodes spcifiques.
INFO
Les informations de type contenues dans les fichiers de classe pour permettre la rflexion du gnrique sont incompatibles avec des machines virtuelles plus anciennes.

Pour exprimer des dclarations de type gnrique, le JDK 5.0 fournit une nouvelle interface Type
dans le package java.lang.reflect. Linterface possde les sous-types suivants :
m

la classe Class, dcrivant des types concrets ;

linterface TypeVariable, dcrivant des variables de type (comme T extends Comparable<?


super T>) ;

linterface WildcardType, dcrivant des jokers (comme ? super T) ;

linterface ParameterizedType, dcrivant la classe gnrique ou les types dinterface (comme


Comparable<? super T>) ;

linterface GenericArrayType, dcrivant des tableaux gnriques (comme T[]).

La Figure 13.5 montre la hirarchie dhritage. Sachez que les quatre derniers sous-types sont des
interfaces (la machine virtuelle instancie des classes adaptes qui implmentent ces interfaces).
LExemple 13.4 utilise lAPI de rflexion gnrique pour afficher ce quil dcouvre sur une classe
donne. Si vous lexcutez avec la classe Pair, vous obtenez ce rapport :
class Pair<T extends java.lang.Object> extends java.lang.Object
public T extends java.lang.Object getSecond()
public void setFirst(T extends java.lang.Object)
public void setSecond(T extends java.lang.Object)
public T extends java.lang.Object getFirst()

Livre Java .book Page 821 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

821

<<interface>>
Type

Class<T>

<<interface>>
TypeVariable

<<interface>>
WildcardType

<<interface>>
Parameterized
Type

<<interface>>
Generic
ArrayType

Figure 13.5
La classe Type et ses descendants.

Si vous lexcutez avec ArrayAlg dans le rpertoire PairTest2, le rapport affiche la mthode
suivante :
public static <T extends java.lang.Comparable> Pair<T extends
java.lang.Comparable> minmax(T extends java.lang.Comparable[])

Les notes API figurant la fin de cette section dcrivent les mthodes utilises dans le programme
dexemple.
Exemple 13.4 : GenericReflectionTest.java
import java.lang.reflect.*;
import java.util.*;
public class GenericReflectionTest
{
public static void main(String[] args)
{
// lire le nom de classe partir des arguments de
// ligne de commande ou de la saisie utilisateur
String name;
if (args.length > 0)
name = args[0];
else
{
Scanner in = new Scanner(System.in);
System.out.println("Enter class name (e.g. java.util.Date): ");
name = in.next();
}
try
{
// imprimer les infos de gnrique pour la classe et
//les mthodes publiques
Class cl = Class.forName(name);
printClass(cl);
for (Method m: cl.getDeclaredMethods())
printMethod(m);
}

Livre Java .book Page 822 Jeudi, 25. novembre 2004 3:04 15

822

Au cur de Java 2 - Notions fondamentales

catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
public static void printClass(Class cl)
{
System.out.print(cl);
printTypes(cl.getTypeParameters(), "<", ", ", ">");
Type sc = cl.getGenericSuperclass();
if (sc!= null)
{
System.out.print(" extends ");
printType(sc);
}
printTypes(cl.getGenericInterfaces(), " implements ", ", ", "");
System.out.println();
}
public static void printMethod(Method m)
{
String name = m.getName();
System.out.print(Modifier.toString(m.getModifiers()));
System.out.print(" ");
printTypes(m.getTypeParameters(), "<", ", ", "> ");
printType(m.getGenericReturnType());
System.out.print(" ");
System.out.print(name);
System.out.print("(");
printTypes(m.getGenericParameterTypes(), "", ", ", "");
System.out.println(")");
}
public static void printTypes(Type[] types, String pre,
String sep, String suf)
{
if (types.length > 0) System.out.print(pre);
for (int i = 0; i < types.length; i++)
{
if (i > 0) System.out.print(sep);
printType(types[i]);
}
if (types.length > 0) System.out.print(suf);
}
public static void printType(Type type)
{
if (type instanceof Class)
{
Class t = (Class) type;
System.out.print(t.getName());
}
else if (type instanceof TypeVariable)
{
TypeVariable t = (TypeVariable) type;
System.out.print(t.getName());

Livre Java .book Page 823 Jeudi, 25. novembre 2004 3:04 15

Chapitre 13

Programmation gnrique

823

printTypes(t.getBounds(), " extends ", " & ", "");


}
else if (type instanceof WildcardType)
{
WildcardType t = (WildcardType) type;
System.out.print("?");
printTypes(t.getLowerBounds(), " extends ", " & ", "");
printTypes(t.getUpperBounds(), " super ", " & ", "");
}
else if (type instanceof ParameterizedType)
{
ParameterizedType t = (ParameterizedType) type;
Type owner = t.getOwnerType();
if (owner!= null) { printType(owner); System.out.print("."); }
printType(t.getRawType());
printTypes(t.getActualTypeArguments(), "<", ", ", ">");
}
else if (type instanceof GenericArrayType)
{
GenericArrayType t = (GenericArrayType) type;
System.out.print("");
printType(t.getGenericComponentType());
System.out.print("[]");
}
}
}
java.lang.Class<T> 1.0

TypeVariable[] getTypeParameters() 5.0

Rcupre les variables de type gnrique si ce type a t dclar comme un type gnrique ou un
tableau de longueur 0 dans les autres cas.

Type getGenericSuperclass() 5.0

Rcupre le type gnrique de la superclasse qui a t dclare pour ce type ou null si ce type
est un Object ou nest pas un type de classe.

Type[] getGenericInterfaces() 5.0

Rcupre les types gnriques des interfaces dclares pour ce type, dans lordre de leur dclaration,
ou un tableau de longueur 0 si ce type nimplmente pas les interfaces.
java.lang.reflect.Method 1.1

TypeVariable[] getTypeParameters() 5.0

Rcupre les variables de type gnrique si cette mthode a t dclare sous forme de mthode
gnrique ou un tableau de longueur 0 dans les autres cas.

Type getGenericReturnType() 5.0

Rcupre le type de retour gnrique avec lequel cette mthode a t dclare.

Type[] getGenericParameterTypes() 5.0

Rcupre les types de paramtres gnriques avec lesquels cette mthode a t dclare. Si la
mthode ne possde pas de paramtres, un tableau de longueur 0 est renvoy.

Livre Java .book Page 824 Jeudi, 25. novembre 2004 3:04 15

824

Au cur de Java 2 - Notions fondamentales

java.lang.reflect.TypeVariable 5.0
String getName()

Rcupre le nom de cette variable de type.

Type[] getBounds()

Rcupre les limites de sous-classe de cette variable de type ou un tableau de longueur 0 si la


variable na pas de limites.
java.lang.reflect.WildcardType> 5.0
Type[] getLowerBounds()

Rcupre les limites de sous-classe (extends) de cette variable de type ou un tableau de


longueur 0 si elle na pas de limites de sous-classe.

Type[] getUpperBounds()

Rcupre les limites de superclasse (super) de cette variable de type ou un tableau de longueur
0 si elle na pas de limites de superclasse.
java.lang.reflect.ParameterizedType 5.0
Type getRawType()

Rcupre le type brut de ce type avec paramtres.

Type[] getActualTypeArguments()

Rcupre les paramtres de type avec lesquels ce type a t dclar.

Type getOwnerType()

Rcupre le type de classe externe sil sagit dun type interne ou null sil sagit dun type de
premier niveau.
java.lang.reflect.GenericArrayType 5.0
Type getGenericComponentType()

Rcupre le type de composant gnrique avec lequel ce type de tableau a t dclar.


Vous arrivez au terme du premier volume de Au cur de Java 2. Il a abord les bases de ce langage
de programmation et les parties de la bibliothque standard dont vous avez besoin pour la plupart de
vos projets de programmation. Nous esprons que vous aurez apprci ce parcours et que vous y
aurez trouv des informations utiles. Pour vous documenter plus avant, par exemple sur la mise en
rseau, le multithreading, la scurit et linternationalisation, consultez le second volume.

Livre Java .book Page 825 Jeudi, 25. novembre 2004 3:04 15

A
Les mots cls de Java

Mots cls

Signification

Voir Chapitre

abstract

Abstraite (pour une classe ou une mthode)

assert

Pour localiser des erreurs internes au programme

11

boolean

Type boolen

break

Sortie dun switch ou dune boucle

byte

Type entier sur 8 bits

case

Clause case dun switch

catch

Clause dun block try interceptant une exception

11

char

Le type de caractre Unicode

class

Dfinition de classe

const

Inutilis

continue

Redmarre une boucle litration suivante

default

Cas par dfaut dun switch

do

Dbut dune boucle do/while

double

Type dun nombre flottant en double prcision

else

Clause else dune instruction if

extends

Dfinit la classe parent dune classe

Livre Java .book Page 826 Jeudi, 25. novembre 2004 3:04 15

826

Au cur de Java 2 - Notions fondamentales

Mots cls

Signification

Voir Chapitre

final

Constante, classe ou mthode qui ne peut tre surcharge

finally

La partie dun bloc try qui est toujours excute

11

float

Type dun nombre flottant en simple prcision

for

Boucle

goto

Inutilis

if

Instruction conditionnelle

implements

Dfinit la ou les interface(s) quune classe implmente

import

Importe un package

instanceof

Teste si un objet est une instance dune classe

int

Type dun entier sur 32 bits

interface

Type abstrait contenant des mthodes devant tre implmentes


par une classe

long

Type dun entier long sur 64 bits

native

Mthode implmente par le systme hte

new

Instancie un nouvel objet ou un tableau

null

Une rfrence vide

package

Un package de classes

private

Restreint la visibilit aux mthodes de la classe

protected

Restreint la visibilit aux mthodes de la classe, ses sous-classes et aux autres classes du mme package

public

Fonctionnalit accessible par les mthodes de toutes les classes

return

Retour dune mthode

short

Type dun entier sur 16 bits

static

Constante ou variable instancie une seule fois, commune


tous les objets de la classe

strictfp

Utilise les rgles strictes pour les calculs virgule flottante

super

Accs la superclasse ou son constructeur

Volume 2

Livre Java .book Page 827 Jeudi, 25. novembre 2004 3:04 15

Chapitre A

Les mots cls de Java

Mots cls

Signification

switch

Instruction de slection

synchronized

Mthode ou bloc de code atomique dans un thread

this

Argument implicite dune mthode ou du constructeur de la classe

throw

Lance une exception

11

throws

Dclare les exceptions que peut lancer une mthode

11

transient

Modificateur des donnes non persistantes

12

try

Partie de code susceptible dintercepter des exceptions

11

void

Qualifie une mthode qui ne renvoie rien

volatile

Sassure quun champ est logiquement accessible par plusieurs


threads

while

Boucle

827

Voir Chapitre
3
Volume 2

Volume 2
3

Livre Java .book Page 828 Jeudi, 25. novembre 2004 3:04 15

Livre Java .book Page 829 Jeudi, 25. novembre 2004 3:04 15

B
Adaptation en amont
du code du JDK 5.0
Cet ouvrage fait appel aux fonctionnalits du JDK 5.0 dans nombre de ses programmes. Cette
annexe reprend les principaux changements apporter aux programmes dexemple de sorte quils se
compilent et sexcutent avec le JDK 1.4.

Amlioration de la boucle for


La nouvelle boucle for (ou boucle "for each") doit tre transforme en boucle traditionnelle.
5.0

1.4

for (variable de type : tableau)

for (int i = 0; i < tableau.length; i++)

{
corps

variable de type = tableau[i];


corps
}

for (variable de type : arrayList)

for (int i = 0; i < arrayList.size();


i++)

{
corps

variable de type = (type) arrayList.get(i);


corps
}

Livre Java .book Page 830 Jeudi, 25. novembre 2004 3:04 15

830

Au cur de Java 2 - Notions fondamentales

Listes de tableaux gnriques


Il convient de supprimer les paramtres de type et dajouter des transtypages toute opration get.
5.0

1.4

ArrayList<Type> arrayList = new ArrayList<Type>()

ArrayList arrayList = new ArrayList();

arrayList.get(i)

(Type) arrayList.get(i)

Autoboxing
Avant le JDK 5.0, la conversion entre les types primitifs et leurs classes enveloppe ntait pas automatique. Pour ce faire, ajoutez un appel de constructeur.
5.0

1.4

Integer wrapper = n;

Integer wrapper = new Integer(n);

Pour transformer une enveloppe en une valeur de type primitif, ajoutez des appels aux mthodes
intValue, doubleValue, etc.
5.0

1.4

Int n = wrapper;

int n = wrapper.intValue();

Listes de paramtres variables


Les paramtres doivent tre regroups dans un tableau.
5.0

1.4

mthode(autres paramtres, p1, p2, p3)

mthode(autres paramtres,
new Type[] { p1, p2, p3 })

Livre Java .book Page 831 Jeudi, 25. novembre 2004 3:04 15

Chapitre B

Adaptation en amont du code du JDK 5.0

831

Types de retour covariants


Avant le JDK 5.0, il ntait pas possible de modifier le type de retour lorsque lon remplaait une
mthode. Aujourdhui, on peut utiliser un sous-type dans la mthode de remplacement. Exemple
type, la mthode clone.
5.0

1.4

public Employee clone() { }

public Object clone() { }

Employee cloned = e.clone();

Employee cloned = (Employee) e.clone();

Importation statique
Limportation statique ntait pas prise en charge avant le JDK 5.0. Ajoutez le nom de classe la
fonctionnalit statique.
5.0

1.4

import static java.lang.Math;

System.out.println(Math.sqrt(math.PI));

import static java.lang.System;


...
out.println(sqrt(PI));

Saisie la console
Avant le JDK 5.0, il nexistait pas de classe Scanner. Utilisez plutt JOptionPane.showInputDialog.
5.0

1.4

Scanner in = new Scanner(System.in);

String input = JOptionPane.showInputDialog(prompt);

System.out.print(prompt);

int n = Integer.parseInt(input);

int n = in.nextInt();

double x = Double.parseDouble(input);

double x = in.nextDouble();

s = input;

String s = in.nextLine();

Livre Java .book Page 832 Jeudi, 25. novembre 2004 3:04 15

832

Au cur de Java 2 - Notions fondamentales

Sortie mise en forme


Avant le JDK 5.0, la mthode printf nexistait pas. Utilisez plutt NumberFormat.getNumberInstance.
5.0

1.4

System.out.printf("%8.2f", x);

NumberFormat formatter
= NumberFormat.getNumberInstance();
formatter.setMinimumFractionDigits(2);
formatter.setMaximumFractionDigits(2);
String formatted = formatter.format(x);
for (int i = formatted.length();
i < 8; i++)
System.out.print(" ");
System.out.print(formatted);

Dlgation du volet conteneur


Avant le JDK 5.0, les classes JFrame, JDialog et JApplet ne dlguaient pas les appels add et
setLayout au volet conteneur. Sachez que ce problme nest signal quau moment de lexcution.
5.0

1.4

add(component)

getContentPane().add(component)

setLayout(manager)

getContentPane().setLayout(manager)

Points de code Unicode


Le JDK 5.0 prend en charge la norme Unicode 4.0, dans laquelle les caractres "supplmentaires"
sont cods avec deux valeurs char conscutives.
5.0

1.4

int cp = string.codePointAt(i)

char cp = string.charAt(i)

if (Character.isSupplementaryCharacter(cp))

omettre les caractres complmentaires nexistaient pas avant le JDK 5.0.

Livre Java .book Page 833 Jeudi, 25. novembre 2004 3:04 15

Chapitre B

Adaptation en amont du code du JDK 5.0

833

Construction des chanes


Le JDK 5.0 introduit une classe StringBuilder dont les mthodes ne sont pas synchronises, ce qui
la rend un peu plus efficace que StringBuffer.
5.0

1.4

StringBuilder

StringBuffer

Livre Java .book Page 834 Jeudi, 25. novembre 2004 3:04 15

Livre Java .book Page 835 Jeudi, 25. novembre 2004 3:04 15

Index

Symboles
> (suprieur ), oprateur 56
= (suprieur ou gal ),
oprateur 56
$, caractre de sparation des
classes 265
$, dans noms de variables et
mthodes 267
&& (et logique), oprateur 56
, (virgule) pour sparer des
fichiers JAR 569
==, oprateur 56
@Override, balise 200
|| (ou logique), oprateur 56

A
Absolue, URL 576
Abstract Window Toolkit Voir
AWT
abstract, mot cl 192
AbstractAction, classe 370,
461, 464
AbstractButton, classe 437,
464, 469
AbstractSpinnerModel, classe
459
Abstraites Voir Classes ou
mthodes
accept, mthode 546, 771
Accs
aux champs privs 178
aux donnes prives 136
aux fichiers, bote de dialogue
534

aux packages 163


aux variables locales 269
protg aux classes 195
verrouillage 780
AccessibleObject, classe 229,
232
Accessoire Voir Composants
Accessor Voir Mthodes
daccs
Accolades dlimitant les blocs
45, 78
Action
icne 463
barre doutils 475
interface 461
fonctions prdfinies 376
mthodes 369
objet, actions prdfinies 369
touches 355
Action, classe 376
ActionEvent, classe 329, 337,
347
ActionListener, interface 270,
327, 329
ActionMap, classe 373
actionPerformed, mthode
327, 330, 336, 461
actionPerformed, mthodeEXactionPerformed,
mthode 327, 329
Activer une option de menu
469
ActiveX 11
scurit 564

Actualisation dune fentre


296
Adaptateurs, classes Voir
Classes
add, mthode 93, 122, 209, 211,
329, 437, 461, 480
addDocumentListener,
mthode 409
addItem, mthode 442, 445
addLayoutComponent,
mthode 510
addWindowListener, mthode
344
AdjustmentEvent, classe 347
Adresses Web Voir Web,
adresses
Affichage
bote de dialogue 525
chane 314
dans un panneau 294
image 320
mise en forme 73
texte 296
after, mthode 120
Agrgation de classes 113
Aide Voir Bulle
Ajout
bouton 329
radio groupe 437
champ de texte 402
composants 294
Algorithmes
CRC 774
fdlibm 59
SHA 747

Livre Java .book Page 836 Jeudi, 25. novembre 2004 3:04 15

836

Index

align, attribut dapplet 567


Alignement, constantes 404
Analyse
classes 225
objets lexcution 228
piles 639
Analyseurs lexicaux 730
Anonymes Voir Tableaux ou
Classes
API
documentation en ligne 68
JNLP 600
notes 66, 290
Preferences 617
Appel
constructeur 154
mthode 143, 184
par valeur/par rfrence 143
append, mthode 430
Appendable, classe 699
Applet
classe 561-562, 573, 576, 580
gestionnaire de scurit 562
AppletContext, classe 570, 585
AppletContext, mthode 580
AppletFrame, classe 586
Applets 14, 20, 39, 512, 588
affichage par le navigateur 579
anciens navigateurs 559
attributs
de code 568
de positionnement 567, 568
des visualisateurs 570
balises
HTML 578
object 571
certifies 563
communication interapplets
570, 578
communication interappletsEXApplets 578
contexte 578, 596
conversion dune application
559
cycle de vie 561
lments de scurit 562
et AWT 556
excution 41

fentres pop-up 564


fichiers JAR 596
initialisation 561
messages davertissement 565
multimdia 576
passer des informations 571
prsentation 554
programmation 281
signet 581
taille 561
Applications
convertir en applets 559
gestion des versions 761
graphiques 37
multithread 13
packages 591
Arborescence
de package 163
des rpertoires Java 27
Architecture
modle-vue-contrleur 386
neutre de Java 11
Archive
attribut dapplet 569
fichier 163
Arithmtiques, oprateurs 54
Array, classe 233
arraycopy, mthode 97, 101
arrayGrow, mthode 234
ArrayIndexOutOfBoundsException, exception 629
ArrayList 208, 211
brut 214
classe 209
classe gnrique 795
tape 214
type 795
Arrays, classe 99, 101, 277
ArrayStoreException, exception
807
Arrire-plan, couleur 307
Arrondi, erreurs 50
ASCII 314
Aspect et comportement Voir
Look and feel
assert, macro 667
Assertions 666
activer 667

dsactiver 667
vrifications 666
AudioClip, classe 118
Augmenter la taille dun tableau
232
Autoboxing 215-216
compilateur 216
available, mthode 695
AWT 282, 312
composants 479
vnements 345
modle dvnement 325
multidiffusion 377
AWTEvent, classe 345

B
Balises
@Override 200
HTML 566
pour la documentation
Voir javadoc
Barre doutils 473
composants 475
icne action 475
sparateur 475
verticale 475
BasicButtonUI, classe 391
BasicService
classe 609
interface 602
BeanBox 275
Beans 275
before, mthode 120
Bibliothque
dexcution 163
de routines 10
installation 26
Java
2D 298
classes 300
standard 157
documentation 27
mathmatique fdlibm 59
publique, code source 26
Swing Voir aussi Swing
BigDecimal, classe 92, 94
BigInteger, classe 92, 94
Binaires, oprateurs 57

Livre Java .book Page 837 Jeudi, 25. novembre 2004 3:04 15

Index

binarySearch, mthode 101, 277


Blocs 78
code source 45
dinitialisation 153
dinstructions 79
Bote sable, Web Start 600
Botes de dialogue
A propos 524
affichage 525
avec aperu 540
cadre propritaire 524, 529
communes 547
composant accessoire 540
conception 487
Couleurs 548
cration 524
de saisie 514
fermeture 525
Fichier 534
classes, constructeurs et
mthodes 545
filtres 536
gestionnaires dvnements 524
mthodes 521
modales/non modales 512, 524
personnalises 524
change de donnes 528
Mot de passe 528
Polices 486, 490
prdfinies 513
messages 514
options 514
types doptions 514
types de messages 514
rutilisation 535
standard 513
taille 524
valeurs par dfaut 528
vue de fichier 538
Boolen, oprateur 56
BorderFactory, classe 438, 440
BorderLayout
classe 395
gestionnaire de mise en forme
394, 480

Bordures
angles arrondis 438
composes 438
titre 438
types de 437
Boucles 82
dtermines 85
do/while 83
for 85
for each 95, 210
quitter les 90
while 82
Bouton
ajout 329
associer une action 371
cration 329
couteur 547
espacement gal 498
icne 514
label 514
mise en forme 392
radio 434
dans menus 464
groupe 434
ajouter un bouton 437
icne 437
Box
classe 481, 485
mthodes statiques 481
conteneur 481
horizontal/vertical 482
mise en forme 483
BoxLayout, gestionnaire de mise
en forme 481
break, instruction 90
bridge, mthode 803
brighter, mthode 307
Buffer, classe 777, 779
BufferedInputStream, mthode
703
BufferedOutputStream, classe
703

837

BufferedOutputStream,
constructeur 703
BufferOverflowException,
exception 777
BufferUnderflowException,
exception 777
Bulle daide 370, 475
ButtonGroup, classe 434, 437
ButtonModel
classe 437
interface 390-391
ButtonPanel, classe 330
ButtonUIListener, classe 391
ByteBuffer, classe 717, 777

C
C# 19
C# 236
C++ 19
#include 159
appels par valeur et par rfrence 147
chanes 64
champs et mthodes statiques
139
classe
string 66
imbrique 260
constructeur 179
virtuel 223
dclaration et dfinition de
variables 53
fonction membre statique 46
hritage 176
multiple 251
liste dinitialisation 151
mthodes 133
en ligne 188
modle de vecteur 209
namespace 159
pointeurs 106, 119
this 153
tableaux 98
transtypage 190
using 159

Livre Java .book Page 838 Jeudi, 25. novembre 2004 3:04 15

838

Index

Cadres 294
affichage 294
ajout de composants 294
contenu 294
coordonnes 289
cration 285
icne 291
position 288
propritaires 524, 529
taille 288
par dfaut 290
Calendar, classe 120
constantes 121
Callbacks 257
classes internes anonymes 260
Voir Rappel
Canaux 772
canRead, mthode 769
canWrite, mthode 769
Caractres
$, dans noms de variables et
mthodes 267
$, de sparation de classes 265
ASCII 314
codage 711
dchappement 51
dcho 410
de remplacement 782
mnmoniques 467
spciaux 51
types de 50
Unicode 51-52, 314, 352
CardLayout, gestionnaire de
mise en forme 481
case, mot cl 90
Cases cocher 431
dans menus 464
cast, mthode 818
catch, clause 222
CD-ROM daccompagnement 5
CENTER, constante 404
Centrage dune chane 315
Chanes 62
affichage 314
centrage 315
concatnation 65
coordonnes 315
dhritage 182

de remplacement 790
dimensions 314
en C++ 64
expressions ordinaires 782
formatage 73
inaltrables 64
motif 782
sous-chanes 63
test dgalit 65
Champ flch 452
Champs
altrables, copie 135
anchor 488
commentaires javadoc 170
contenu des 228
dinstance 111
et variables locales 133
final 136
modification 134
de donnes 112, 224, 229
altrables 135
copie 251
initialisation 153
privs 134, 136
publics 131
de la classe 137
de mot de passe 410
de poids 488
de texte 401, 402
couteur 406
initialisation 402
modification 405
descripteurs 748
en lecture seule 134
fill 488
filtres 412
final 187
gridheight 489
gridwidth 489
initialisation 149
explicite 151
insets 489
privs 136, 228, 267
accs 178, 195
saisie numrique mise en forme
411
statiques 137, 141
initialisation 154

changedUpdate, mthode 409


charAt, mthode 66
CharBuffer, classe 717, 779
Chargeur de classe 276
CharSequence, classe 700
Charset, classe 717
Checked exception Voir
Exception vrifie
checkError, mthode 720
Chemin
de classe Voir CLASSPATH
dexcution 25
Choix doptions 431
Class
classe 819, 220, 227, 236, 596,
823
classe gnrique 818
objet 221
mot cl 44
paramtre 819
Classes
abstraites 190, 250, 401
InputStream 694
OutputStream 694
accs protg 195
adaptateurs 342, 346
agrgation 113
ajout dans un package 160
analyse 225
des caractristiques 223
anonymes 267, 334, 343
bibliothque Java 2D 300
C++ 46
caractre de sparation ($) 265
chargeur de 276
chemin daccs Voir CLASSPATH
chemin de 165
commentaires javadoc 168
conception 172
concrtes 300
et abstraites 190
ConsoleWindow 676
cration lexcution 275
dexception 632
de base Voir Superclasses
dfinies par lutilisateur 167
dpendance 113

Livre Java .book Page 839 Jeudi, 25. novembre 2004 3:04 15

Index

drivation 176
drives Voir Sous-classes
diagrammes 114
encapsulation 176
enfant Voir Sous-classes
numration 239
enveloppes 276, 390
extension 112, 176
Externalizable 748
externes, rfrence 264
extraction des informations 224
FilterInputStream 701
FilterOutputStream 701
final 187
gnriques 208
ArrayList 795
dfinition 796
hritage 809
hritage Voir Hritage
hirarchie 182, 694
identificateur 747
imbriques 273
en C++ 260
inaltrables 252
internes 260, 333
anonymes 260, 270, 334, 343
constructeurs 263
dclaration 273
tat dun objet 261
locales 267
porte 265
prives 262
scurit 265
statiques 272
syntaxe 261
istream 696
Java 46
java.io 700
locales
mthodes 269
visibilit 268
localisation par la machine
virtuelle 163
OutputStreamWriter 709
parent Voir Superclasses
personnalises 127

proxy 276
proprits 279
RandomAccessFile 707
relation entre classes 113
rpertoires 163
rutilisation 116
srialisables 758-759
sous-classes Voir Sous-classes
statiques internes 272
stub 275
superclasses Voir Superclasses
Swing 286
systme 164
Timer 257
visibilit 262
Voir aussi nom de la classe
ClassLoader, classe 670
CLASSPATH 31, 163
Clause
finally 636
throws 627
Clavier
vnements 350
touches daction 355
Clonage 207, 763
dobjets 251
et copie 252
exceptions 255
superficiel 251
clone, mthode 207, 250-251
Cloneable, interface 250, 253
close, mthode 696
Closeable, classe 699
closeEntry, mthode 727
COBOL 175
Codage
des caractres 711
jeux de caractres 709
rfrence ISO 710
Code
attribut dapplet 568
de touche 351
des exemples 5
existant 804
fichiers bibliothque 26
HTML 40
point 50

839

stub 275
units 50
codebase, attribut dapplet 569
codePointAt, mthode 63
Colonnes, aligner 499
Color
classe 306, 309
objet 307
ColorAction, classe 331
com.sun.java, package 338
Commentaires
pour la documentation
Voir javadoc
dans code 47
Comparable, interface 245, 248,
277, 799
compareTo, mthode 66, 244-245,
248, 277
Compilateur 12
installation 23
javac 29, 165
localisation de fichiers 164
option -classpath 163
Compilation
dans diteur de texte 35
erreurs 34
instructions 24
Component, classe 288, 307, 323,
364, 367, 403, 485
hritage 480
ComponentEvent, classe 350
Composants
accessoires 540
ajout dans un cadre 294
AWT 479
barre doutils 475
coordonnes 289
dinterface graphique, caractristiques 386
focus Voir Focalisation
lourds et lgers 283
mise en forme 392
oppos 367
Swing 390, 540
Compression, pack200 590
Concatnation de chanes 65
Conception de classes 172

Livre Java .book Page 840 Jeudi, 25. novembre 2004 3:04 15

840

Index

Concordance
de proprits 611
fichiers par mmoire 772
Concrtes Voir Classes
ou Mthodes
Conditionnelles, instructions 79
Configuration
chemin dexcution 25
de base de Java JDK 28
emplacement des fichiers 618
paires cl/valeur 611
prfrences dapplication 611
Conflits aprs effacement 808
Consignation 649
API 649
basique 650
exceptions 652
gestionnaires 654
hirarchie 650
localisation des messages 653
modifier la configuration 652
niveaux 651
rappels 659
ConsoleHandler, classe 665
ConsoleWindow, classe 676
const, mot cl 54
Constantes 54
code de touche 351
mathmatiques 58
statiques 138
Constraints, classe 505
Constructeurs 117, 131, 709
appel 154
dun autre constructeur 152
bouton 329
classes internes 263
gnralits 131
par dfaut 150
Construction
dune ellipse 303
dune ligne 303
Constructor, classe 223, 227, 819
gnrique 819
Container, classe 333, 393, 395,
480
Conteneurs 286, 396
avec onglets 481
contentPane, classe 394

Contenu
dun cadre 294
dun champ 228
Contextes statiques 808
continue, instruction 92
Contrleur de composant
dinterface 386
Conventions du livre 5
Conversion
classes 122
entiers 52
mthode printf 74
type
flottant 60
numrique 59, 188
primitif 215
type Voir aussi Transtypage
Coordonnes
cadre/composants 289
dune chane 315
dune ellipse 303
vnements de la souris 364
grille GridBagLayout 488
Java 2D 299
objet Graphics 296
Copie
et clonage 252
intgrale 252
tableaux 97
variable 251
copyArea, mthode 319, 322
Couleurs 306
darrire-plan 307
par dfaut 308
personnalises 307
slection 547
systme 308
countTokens, mthode 731
createBevelBorder, mthode 440
createCompoundBorder,
mthode 438, 441
createCustomCursor, mthode
360, 364
createDialog, mthode 552
createEmptyBorder, mthode
440
createEtchedBorder, mthode
440

createGlue, mthode 485


createGlue, mthodeEXcreateGlue, mthode 485
createHorizontalBox, mthode
485
createHorizontalGlue, mthode
485
createHorizontalStrut, mthode
485
createImage, mthode 323
createLineBorder, mthode 440
createLoweredBevelBorder,
mthode 440
createMatteBorder, mthode 440
createNewFile, mthode 771
createRaisedBevelBorder,
mthode 440
createRigidArea, mthode 485
createScreenCapture, mthode
682
createTempFile, mthode 769
createTitledBorder, mthode 438,
441
createVerticalBox, mthode 485
createVerticalGlue, mthode 485
createVerticalStrut, mthode 485
Cration
bote de dialogue 524
bouton 329
curseur 445
GridBagLayout 489
panneau 296
systme de menu 88
Curseurs 358
cration 445
repres 446, 451
Cursor, classe 358

D
darker, mthode 307
DataInput, classe 705
DataInputStream, sous-classe
698, 707
DataOutput
classe 706
interface 704, 737
DataOutputStream, sous-classe
698

Livre Java .book Page 841 Jeudi, 25. novembre 2004 3:04 15

Index

Date, classe 117


DateEditor, classe 459
DateFormat, classe 424
de marquage 253
Dbogage
assertions 666
techniques de mise au point 670
Dbogueur
Eclipse 691
JDB 685
Dcalage, oprateurs 58
Dclaration
classe 153, 187, 272
abstraite 191
final 187
interne 273
locale 268
interface 245
matrice 102
mthode 133
publique 186
virtuelle 180
tableaux 95
variables 47
Dcrmentation, oprateurs 56
Deep copy Voir Copie intgrale
default, mot cl 90
DefaultButtonModel, classe 390
DefaultFormatter
classe 416, 425
mode overwrite 416
Dfinition
mthodes 133
variables 53
Dlgation dvnement 328, 349
Dlgu 236
delete, mthode 769
deleteOnExit, mthode 769
Dpendance de classes 113
Dprcies Voir Mthodes
deriveFont, mthode 314
Driver une classe 176
Dsactiver une option de menu
469
Dsallouer une ressource 636
Descripteur de champ 748
Dessiner en Java 295

destroy, mthode dapplet 562


Destruction des objets 157
Diagrammes de classe 114
Dichotomique, recherche 278
Dictionary, classe 805
dispose, mthode 157, 288, 322
do/while 83
doclet 172
docs, rpertoire 27
Document, interface 405
Documentation
API en ligne 68
commentaires Voir javadoc
installation 26
DocumentEvent, interface 409
DocumentFilter, classe 425
DocumentListener, interface 409
Donnes
change 528
prives, accs 136
publiques 136
types de 47
double, type 55
Double.parseDouble, mthode 72
Drapeaux 75
draw, mthode 299
drawImage, mthode 319, 322
drawRectangle, mthode 299
drawString, mthode 296, 318
Dump 690
Dynamique
HTML 14
liaison Voir Liaison
tableau 232

E
E/S, nouvelles 771
EAST, constante 404
Echappement, caractres 51
Eclipse 32
Ecouteur 327
bouton 547
case cocher 432
champ de texte 406
dvnement 326
notification 326

841

transformer 336
de changement de proprit 370,
540
de plusieurs sources dvnement 369
et vnements, relation 350
menus 460
multiple 377
recensement 327
Ecran, taille 290
Editeur de texte, compilation et
excution dans 35
Effacement
conflits 808
type 802
type de retour 802
types gnriques 819
variables de type 801
Ellipse2D, classe 302
Ellipses, construction 303
else, mot cl 80
Emacs 28, 35
Empcher lhritage 187
Employee, classe 131
EmployeeTest, classe 128
Empty, style de bordure 438
Encapsulation 111, 115, 172, 176
endsWith, mthode 67
ensureCapacity, mthode 208
Entiers 48
saisie 411
Entre
de texte utilisateur 401
lecture 70
utilisateur, bote de dialogue 528
entries, mthode 729
Enumration 61
classes 239
sre 760
type 239
Enveloppes 215
Voir Classes
Environnements de dveloppement 28, 392
intgrs 28
Epoch, point fixe 120
equals 198

Livre Java .book Page 842 Jeudi, 25. novembre 2004 3:04 15

842

Index

equals, mthode 67, 101, 197, 280


dObject 207
proprits 198
equalsIgnoreCase, mthode 65,
67
Erreurs
darrondi 50
compilation 159, 179
localisation 34
recherche 30
traitement 626
errout, tlchargement 32
Etat
dun objet 111, 261
des touches 352
EtchedBevel, style de bordure
438
Etendre une classe 112
Etiquettes 403
HTML 404
icnes 405
instructions 91
Evaluation optimise 56
Evnements
action 327
AWT 345, 346, 678
modle 325
classes et mthodes 348
classes internes 334
de bas niveau 347
de bouton radio 434
de case cocher 432
de fentre 341
de focalisation 365
de la souris 356
de liste combine 443
dlgation 328, 349
du clavier 350
couteurs de plusieurs sources
369
et couteurs, relation 350
tat des touches 352
gnralits 326
gestion 349
implmenter les sources 380
interaction 328
objets 326
smantiques 347

source des 326


souris 350
coordonnes 364
Event listener Voir Ecouteur
dvnement
EventHandler, classe 337
EventListenerList, classe 380,
384
EventObject, classe 336, 337, 345
EventTrace, exception 678
ExampleFileFilter, classe 538
Exception
ArrayIndexOutOfBoundsException 629
capture des exceptions 633
ClassCast 233
classe 222, 641
classement 627
classes 632
Throwable, Error et
Exception 627
clause
catch 222
finally 636
conseils pour leur utilisation 646
consignation 652
en C++ 639
enchaner 635
EventTrace 678
gnralits 626
gestionnaire 220, 222
getCause 636
hirarchie 628
IllegalAccessException 228
interception 221
lancement 631
lancementEXExceptions
lancement 631
look and feel 338
multiples 635
non vrifies 221
NumberFormatException 406
programmation gnrique 806
relancer une exception 635
RuntimeException 630
scurit 616
signalement au compilateur 629
try 222

vrifies 220, 253


Excution
bloc 79
dans diteur de texte 35
de programmes dans une page
Web 20, 39
identification de type 220
interrompre 90
rflexion 219
Exemples de programmes
installation 26
exists, mthode 769
Explicites Voir Paramtres
Expressions
gnriques, traduction 802
ordinaires 782
syntaxe 783
extends, mot cl 176
Extension de classes 176
ExtensionFileFilter, classe 537
Externalizable
classe 748
interface 759
Externes Voir Objets ou classes
Extraction des commentaires 171

F
Factory Voir Mthodes
FAQ 21
fdlibm, bibliothque
mathmatique 59
Fentre
active 365
actualisation 296
gestionnaire 365
oppose 367
Fermeture
bote de dialogue 525
Java 157
programme 37
Fichiers
archive 163
botes de dialogue
pour slection 534
caractre de sparation 700
classe File 766
concordance de mmoire 772

Livre Java .book Page 843 Jeudi, 25. novembre 2004 3:04 15

Index

gestion du systme de fichiers


766
JAR Voir JAR
multimdia 577
objets srialiss 746
verrouillage 780
ZIP 721
criture 726
lecture 721
Fichiers journaux
filtres 658
formateurs 658
Field, classe 223, 227-228
File
classe 537, 766, 769
objet 535
FileChannel, classe 776, 781
FileContents, classe 610
FileFilter
classe 546
interface de java.io 537
FileHandler, classe 665
FileInputStream
classe 702,776
constructeur 702
FileLock, classe 781
FilenameFilter
classe 771
interface 768
FileNotFoundException,
exception 603, 610
FileOpenService, classe 610
FileOutputStream
classe 703,776
constructeur 703
FileSaveService, classe 610
FileView, classe 538, 539, 546
mthodes 538
fill, mthode 101, 309
Fillers Voir Rserves
Filter, classe 666
FilterInputStream, classe 701
FilterOutputStream, classe 701
Filtres 412, 536
All files (Tous fichiers) 538
champs de texte 412
de documents 413

fichiers journaux 658


journaux 650
pluggable 414
final
champ dinstance 136
mot cl 54, 180, 187, 268, 269
finalize, mthode 157
finally, clause 636
FlowLayout
classe 394
constructeur 394
gestionnaire de mise en forme
392, 481
flush, mthode 695
Flushable, classe 699
Flux 729
classe StringBuilder 735
dentre 694
dobjets 742
de donnes 704
de fichiers ZIP 721
de sortie 694
de texte 708
dlimiteurs 729, 730
criture
au format texte 720
dobjets de types variables
742
des rfrences dobjets 750
en format fixe 729
en accs direct 736
filtrs 700
gnralits 693
lecture en format fixe 731
sortie des rfrences dobjets
756
Focalisation 365, 406
comportement du champ 411
composants 347, 365
de saisie 511
ordre de 365, 511
Focus Voir Focalisation
FocusEvent, classe 347, 368
Fonctions
de rappel 239
mathmatiques 58
membres statiques C++ 46

843

Font
classe 311, 313-314
constructeur 317
Fontes 311
disponibles 311
logiques 312-313
taille 313
TrueType 313
FontMetrics, classe 318
FontRenderContext, classe 314
for each, boucle 95
for, instruction 85
Formateurs
fichiers journaux 658
personnaliss 417
Formes
2D 298
remplissage 309
forName, mthode 220, 223
Frame, classe 285, 293
Frames Voir Cadres

G
Garbage collector Voir Ramassemiettes
General path 299
GenericArrayType, classe 824
Gestion
dvnement, gnralits 326
de versions 761
des fichiers 766
Gestionnaires
consignation 654
dvnement bote de dialogue
524
dexception 220
implmentation 222
dinvocation 276
de fentre 365
de mise en forme 392, 474
BorderLayout 394, 480
BoxLayout 481
CardLayout 481
FlowLayout 392, 481
GridBagLayout 481, 486
OverlayLayout 481

Livre Java .book Page 844 Jeudi, 25. novembre 2004 3:04 15

844

Index

Gestionnaires (suite)
personnaliss 507
Swing 479
de scurit 562
applet 562
par dfaut 655
paramtres 655
get, mthode 122, 228
champs 125
getAbsolutePath, mthode 768,
769
getActionCommand, mthode
337, 435, 437
getActionMap, mthode 377
getAlignmentX, mthode 485
getAlignmentY, mthode 485
getApplet, mthode 580
getAppletContext, mthode 580
getAppletInfo, mthode 576
getApplets, mthode 580
getAscent, mthode 318
getAudioClip, mthode 577, 578
getAvailableFontFamilyNames,
mthode 311
getCanonicalFile, mthode 769
getCanonicalPath, mthode 768,
769
getCause 636
getClass, mthode 207, 220, 743
getClickCount, mthode 357, 364
getCodeBase, mthode 578
getColor, mthode 552
getComponent, mthode 350, 366
getComponentType, mthode 233
getConstraint, mthode 499, 500
getConstructors, mthode 224,
227
getCrc, mthode 728
getDeclaredConstructors,
mthode 224, 227
getDeclaredFields, mthode 224,
227
getDeclaredMethods, mthode
224, 227
getDeclaringClass, mthode 227
getDescent, mthode 318
getDescription, mthode 546
getDocument, mthode 409

getDocumentBase, mthode 578


getEntry, mthode 729
getExceptionTypes, mthode 227
getFamily, mthode 317
getFields, mthode 224, 227
getFilePointer, mthode 708
getFirst, mthode 812
getFontMetrics, mthode 318
getFontName, mthode 317
getFontRenderContext, mthode
314, 318
getGregorianChange, mthode
126
getHeight, mthode 318
getIcon, mthode 546
getImage, mthode 293, 577, 578
getInputMap, mthode 372, 377
getInputStream, mthode 729
getKeyChar, mthode 355
getKeyCode, mthode 355
getKeyModifiersText, mthode
356
getKeyStroke, mthode 372, 376
getKeyText, mthode 356
getLeading, mthode 318
getLength, mthode 233, 409
getLineMetrics, mthode 315
getLocalGraphicsEnvironment,
mthode 311
getLocation, mthode 292
getLocationOnScreen, mthode
292
getMethods, mthode 224, 227,
236
getModifiers, mthode 223, 227,
356
getModifiersEx, mthode 364
getModifiersExText, mthode 364
getName, mthode 220-221, 227,
237, 769
getName, mthode de Font 317
getNextValue, mthode 454
getOppositeComponent, mthode
367
getOppositeWindow, mthode
367
getParameter, mthode 573
getParameterInfo, mthode 576

getParameterTypes, mthode 227


getParent, mthode 770
getParentFile, mthode 770
getPassword, mthode 410
getPath, mthode 536, 770
getPredefinedCursor, mthode
358
getPreviousValue, mthode 454
getProxyClass, mthode 280
getResourceAsStream, mthode
594
getScreenSize, mthode 293
getSelectedFiles, mthode 536
getSelectedItem, mthode 442,
443, 445
getSelectedObjects, mthode 435
getSelection, mthode 435, 437
getSize, mthode 292
getSource, mthode 336, 337, 350
getStackTrace, mthode 640
getStringBounds, mthode 314
getText, mthode 402, 409
getTime, mthode 122
getType, mthode 223
getTypeDescription, mthode 546
getValue, mthode 369, 452
dAction 376
getWidth, mthode 300, 301, 315
getX, mthode 357
getY, mthode 357
GifFilter, classe 538
Glue 482
GMT 120
GNU Emacs, site Web 35
goto, mot rserv 90
Grands nombres 47, 92
Graphic User Interface Voir GUI
et Interface
Graphics
classe 296, 298, 309, 318-319,
322
objet 295
Graphics2D, classe 298
GraphicsEnvironment, classe
311, 684
Graphiques, applications 37

Livre Java .book Page 845 Jeudi, 25. novembre 2004 3:04 15

Index

GregorianCalendar
classe 120, 125
constructeur 125
GridBagConstraints, classe 490,
496
contraintes 487
GridBagLayout
classe daide 490
contraintes 495
gestionnaire de mise en forme
481, 486
objet
champs 488
cration 489
GridLayout
classe 480
constructeur 401
Groupe de boutons radio 434,
437
GUI 282, 326
composants 349
caractristiques 386
gnralits 282

H
Handler, classe 664
hashCode 200
hashCode, mthode 280
Hashtable, classe 804
hasMoreTokens, mthode 731
height 290
height, attributs dapplets 567
Helper Voir Mthodes
Hritage 113, 175
chane d 182
classes
Component 480
et sous-classes 190
Java 2D 300
JFrame 288
JPanel 288
classes gnriques 809
empcher 187
equals 198
gnralits 176
hirarchie 182
utilisation 241

Hirarchie
classe Shape 302
dhritage 182
des oprateurs 60
vnements AWT 345
Historique de Java 15
Holder, type 217
Hot spot Voir Point chaud
HTML 18, 554
balises 554, 566
boutons, tiquettes, options de
menu 404
dynamique 14
label 333

I
I/O 771
IANA
Registre des jeux de caractres
709
Icne 369, 540
action 463
dans barre doutils 475
bote de dialogue 514
bouton 329, 514
radio 437
cadre 291
de fentre 288
label 404
menu 462-463
option de menu 370
vue de fichier 539
Ides fausses sur Java 18
Identificateurs de classe 747
if, instruction 79
if/else if 81
IllegalAccessException 228
Image
classe 322
objet 288
ImageIcon
classe 333
constructeur 333
ImageIO, classe 321

845

Images 319
affichage 320
en mosaque 319
fractales 319
mise lchelle 322
implements, mot cl 245
Implicites Voir Paramtres
Imports
instruction 158
statiques 159
Inaltrables Voir Chanes ou
Classes
Incrmentation, oprateurs 56
indexOf, mthode 67
Indice, tableau 94
Informations de configuration
617
Informations relatives une
classe 224
init, mthode dapplet 561
Initialisation
blocs 153
champs
de donnes 153
de texte 402
explicites 151
par dfaut 149
statique 154
tableaux 96
variables 53
Inlining 187
InputEvent, classe 356, 364
InputStream, classe 694, 695
InputStreamReader, classe 709
insertItemAt, mthode 442, 445
insertSeparator, mthode 462
insertUpdate, mthode 410
Installation
bibliothque et documentation
26
compilateur et outils 23
exemples de programmes 26
instructions 24
messages 25
rpertoire par dfaut 24
instanceof, oprateur 189, 249
Instanciation 807

Livre Java .book Page 846 Jeudi, 25. novembre 2004 3:04 15

846

Index

Instructions
blocs 79
break 90
composes 79
conditionnelles 79
continue 92
dinstallation et de compilation
24
tiquetes 91
goto 90
import 158
switch 88
Integer
classe 217, 277
type 47
Integer.parseInt, mthode 72
Interception dexceptions 221
Interface 244
callbacks 257
Comparable 244
de balisage 253
dclaration 245
couteur 327
graphique
activer/dsactiver des options
469
bote de dialogue 512
Fichier 534
bordures 437
boutons radio 434
cases cocher 431
champs
de mot de passe 410
de texte 402
composants 386
avec GridBagLayout 488
conteneurs 396
entre de texte 401
tiquettes 403
vnements 325
listes doptions 442
menus 460
contextuels 465
mises en forme 392
sophistiques 478
raccourcis clavier 467
rserves entre lments 482
squence de tabulation 511

utilisateur Voir GUI


validation dentre utilisateur
410
zones de texte 426
graphique utilisateur 282
graphique Voir aussi Gestionnaires de mise en forme
mthodes 244
proprits 249
publique 178
utilisateur graphique Voir GUI
InterfaceEXInterface 244
Interfaces 253
de flux 699
couteurs 346
Voir aussi nom de linterface
Internet et Java 14
Interprteur 12
Interruption du flux dexcution
90
Intervalles, mise en forme 398
Intranet 555
intValue, mthode 217
InvocationHandler, interface 276
invoke, mthode 236, 276
IOException, exception 699
isAccessible, mthode 232
isActionKey, mthode 355
isAltDown, mthode 352
isControlDown, mthode 352
isDirectory, mthode 767, 770
isEnabled, mthode 292, 376
isFile, mthode 767, 770
isFocusTraversable, mthode 367
isHidden, mthode 770
isJavaIdentifierPart, mthode 52
isJavaIdentifierStart, mthode 52
isMetaDown, mthode 352
ISO, codage 710
isProxyClass, mthode 280
isSelected, mthode 431, 433, 465
isShiftDown, mthode 352
isShowing, mthode 292
isTemporary, mthode 366
isTraversable, mthode 538
istream, classe 696
ItemEvent, classe 347
ItemSelectable, interface 435

J
J++ 13, 236
Jambage ascendant/descendant
314
JAR 5
fichiers 26, 163, 167, 589
programme darchivage 589
utilisation 26
Java
applications et applets 583
architecture neutre 11
bibliothque de routines 10
et C# 19
et C++ 19
excuter un programme dans
une page Web 20
historique 15
ides fausses 18
installation des outils 23
Internet 14
J++ 13
livre blanc 8
machine virtuelle 16
mcanismes de scurit 564
modle de pointeur 10
orient objet 9
plug-in 555
portabilit 12
rpertoires 27
scurit 20
syntaxe 9
termes cls 8
Java 2D
bibliothque 298
types de coordonnes 299
Java Web Start 603
java, package 157
java.awt, package 166
java.awt.Color, classe 306
java.awt.event, package 347
java.io
classe 700
package 537
java.lang, package 66, 71, 164
java.lang.Object 202
java.math, package 92
java.nio, package 771
java.sql, package 159

Livre Java .book Page 847 Jeudi, 25. novembre 2004 3:04 15

Index

java.util, package 71, 158, 345


java.util.jar, package 158
JavaBeans 540
et rflexion 219
javac, compilateur 165
javadoc 3, 167
balises 169
doc en ligne 172
extraction des commentaires
171
insertion des commentaires 168
JavaScript 21, 569
javax, package 157, 286
javax.swing, package 72, 286-287,
338
javax.swing.event, package 346,
469
javax.swing.filechooser, package
537, 538
javax.swing.filechooser.FileFilter, interface 537
JBuilder 28
JButton
classe 333, 390-391, 534
constructeur 333
JCheckBox, classe 433
JCheckBoxMenuItem
classe 465
constructeur 465
JColorChooser, classe 547, 552
JComboBox, classe 442, 445
JComponent, classe 295, 377,
391, 403, 409, 437, 441, 466, 512,
534
JDE, package 35
JDialog
classe 524, 528
constructeur 528
JDK 23
configuration de base 28
JEditorPane, classe 428
Jeux de caractres 709
IANA 709
JFC 282
JFileChooser
classe 513, 534, 545
constructeur 545

JFormattedTextField 415
classe 424
JFormattedTextFieldAbstractFormatter, classe 425
JFrame, classe 285, 294, 298, 307,
394, 462
hirarchie dhritage 288
JFrame.add, mthode 294
JLabel, classe 403-404
JLayeredPane 294
JMenu
classe 376, 461
constructeur 461
JMenu.add, mthode 460
JMenuItem
classe 462, 464, 468, 470
constructeur 462, 468
JNLP, Java Network Launch
Protocol 598
API 600
Jokers 810
capturer 815
limites 812
sans limites 814
JOptionPane 259
72-73, 513, 521
JOptionPane.showInputDialog,
mthode 72
Journaux
enregistrements 649
filtres 650
mise en forme 650
JPanel
classe 434, 481
hirarchie dhritage 288
constructeur 397
JPasswordField, classe 410
JPasswordField, classeEXJPasswordField, classe 410
JPopupMenu, classe 466
JRadioButton, classe 434, 437
JRadioButtonMenuItem
classe 465
constructeur 465
JRoot 294
JRootPane, classe 534
JScrollPane, classe 430

847

JSlider, classe 450


JSpinner, classe 452, 458
JSpinner, composant 451
JTabbedPane 481
JTextArea
classe 426
composant 401
constructeur 429
JTextComponent, classe 401
JTextField, classe 390, 403
JTextField, composant 401
JTextPane, classe 428
JToolBar, classe 475, 478
JVM Voir Machine virtuelle

K
KeyboardFocusManager, classe
368
KeyEvent, classe 347
KeyListener, interface 350
keyPressed, mthode 350, 352
keyReleased, mthode 350
KeyStroke, classe 371, 376, 468
keyTyped, mthode 350, 352
Kit de dveloppement Java JDK
Voir JDK

L
Label
bouton 514
icne 404
texte 405
Voir aussi Etiquettes
Langages
de script 21
Java 55, 122, 246
appel de mthodes 143
constructeurs 117
mots cls 4
Pascal 110
procduraux 9, 110
Visual Basic 326
lastIndexOf, mthode 67
lastModified, mthode 770
layoutContainer, mthode 510

Livre Java .book Page 848 Jeudi, 25. novembre 2004 3:04 15

848

Index

LayoutManager
classe personnalise 507
interface 510
mthodes 507
LayoutManager2, interface 507
Lecture
des entres 70
seule, champs 134
LEFT, constante 393, 404
length, mthode 67, 212
de RandomAccessFile 708
Liaison
dynamique 180, 184
statique 185
Libells Voir Etiquettes ou Label
Ligne
construction 303
de base 314
de commande
outils 29
paramtres 98
de sparation, menus 462
Limites
jokers 812
supertypes 812, 813
variables de types 798
Line, style de bordure 438
LineMetrics, classe 315
Linux 25
list, mthode de File 770
Listener interface Voir Interface
couteur
Listes
doptions 442
suppression/ajout dlment
443
de tableaux 95
accs aux lments 210
allocation 209
capacit 209
gnriques 207
Integer 215
listFiles, mthode 537, 770
listRoots, mthode 770
Localisation
arguments 654
de fichiers par le compilateur
164

des classes dans les packages


159
des erreurs de compilation 34
messages de consignation 653
Logger, classe 663
LogRecord, classe 665
Longueur des tableaux 97
Look and feel 283, 390, 514
disponibles 338
Mac 338
Metal 284, 338
modification 338
Motif 283, 338
Windows 283, 338
LoweredBevel, style de bordure
438

M
Machine virtuelle 16
architecture neutre 11
effacement de types gnriques
819
localisation des classes 163
programmation gnrique 800
main, mthode 46, 140
chargement 220
makeButton, mthode 334
Manager, classe 176
mark, mthode 696
markSupported, mthode 696
MaskFormatter, classe 416, 426
Matcher, classe 791
Math, classe 58
constantes 138
Math.random, mthode 100
Mathmatiques, fonctions et
constantes 58
Matrice 102
Matte, style de bordure 438
Mcanisme de rflexion 223
Media tracker Voir Pisteur de
mdia
MediaTracker, classe 320, 323
Mmoire
concordance pour les fichiers
772
virtuelle 772
menuCanceled, mthode 470

menuDeselected, mthode 470


MenuListener, classe 470
Menus 459
activer et dsactiver des options
469
avec cases cocher et boutons
radio 464
contextuels 465
cration 88, 460
droulants, mthodes 466
couteur 460
icne 463
imbriqus 470
ligne de sparation 462
Macintosh 461
options 463
raccourcis 467
sous-menus 460
supprimer des options 469
Windows 461
Messages
linstallation 25
botes de dialogue 514
derreur 31
sur plusieurs lignes 515
Mta-donnes 200
Metal 284, 338
Method, classe 223, 227, 236, 823
Mthodes 46, 111
abstraites 191
accs aux donnes prives 136
appel 184
par rfrence 143
par valeur 143
botes de dialogue 521
caractres $ dans nom 267
classe
locale 269
commentaires javadoc 169
concrtes 191
createScreenCapture 682
daccs 121, 134
interface ButtonModel 391
daltration 121
dune interface 244
de RectangularShape 302
dclaration 133
dfinition 133

Livre Java .book Page 849 Jeudi, 25. novembre 2004 3:04 15

Index

dprcies 120, 157, 287


distantes 275
en ligne 133
factory 140
final 187
flush 695
gnriques
dfinition 797
traduction 802
hashCode 200
helper 334
natives 138
nombre variable de paramtres
218
paramtres 46, 143, 217
pointeurs 236
prives 131, 136
readExternal 759
readline 732
signature 149, 185
statiques 58, 137, 141, 233, 249,
267, 311, 338
classe Box 481
superclasse/sous-classe 177
surcharge 149
syntaxe 46
tables 186
virtuelles 180
visibilit 186, 245
Voir aussi nom de la mthode
writeChars 705
minimumLayoutSize, mthode
510
Mise en forme
conteneur Box 483
dinterfaces
introduction 392
sophistiques 478
de laffichage 73
formateurs personnaliss 417
intervalles 398
NumberFormat 415
personnalise 479, 507
sans gestionnaire 506
spring 496
Mix-in 251
mkdirs, mthodes 771

Modale/non modale Voir Botes


de dialogue
Modle
dvnement
AWT 325
de composant dinterface 386
de conception 388
Modle-vue-contrleur Voir
Architecture
Modificateurs de visibilit 196
Modification
champ dinstance 134
look and feel 338
Modifier, classe 223
Mosaque 319
Motif 283, 338
Motif, chanes 782
Mots cls
abstract 192
case 90
class 44
const 54
default 90
else 80
extends 176
final 54, 180, 187, 268, 269
implements 245
import 159
langage Java 4
package 159
private 131, 136
protected 195, 241
public 44, 130, 131, 253
redondance 250
static 139, 154
strictfp 55
super 178
this 133, 152, 179, 265
void 46
MouseAdapter, classe 361
mouseClicked, mthode 357
mouseDragged, mthode 360
mouseEntered, mthode 360
MouseEvent, classe 347, 364, 466
mouseExited, mthode 360
MouseListener
classe 350

849

interface 360
MouseMotionListener
classe 350
interface 360
mouseMoved, mthode 360
mousePressed, mthode 357
mouseReleased, mthode 357
MouseWheelEvent, classe 347
Multicasting Voir Multidiffusion
Multidiffusion 377
Multimdias, fichiers 577
multiply, mthode 93
Multithread 13
Mutator Voir Mthodes daltration

N
name, attribut dapplet 569
Natives Voir Mthodes
NetBeans 28
new, oprateur 131
newInstance, mthode 222, 223,
275
newProxyInstance, mthode 276,
280
nextDouble, mthode 71
nextInt, mthode 71
nextLine, mthode 71
nextToken, mthode 731
Niveaux de consignation 651
Noms
de packages 157
de paramtres 152
de variables et mthodes ($) 267
NORTH, constante 404
NoSuchElementException,
exception 731
Notes API 66, 290
Notification aux couteurs
dvnement 326
NotSerializableException, exception 758
null, valeur 119
Number, classe 215
NumberFormat, classe 218
NumberFormatException 406

Livre Java .book Page 850 Jeudi, 25. novembre 2004 3:04 15

850

Index

O
object
attribut dapplet 569
balise HTML 571
classe 196, 207, 276
ObjectInputStream
classe 746
constructeur 746
mthode 742
ObjectOutputStream
classe 742, 746
constructeur 746
objet reader 727
Objets 112
Action, actions prdfinies 369
analyse lexcution 228
champs dinstance 111
Class 221
clonage 251
Color 307
construction 117, 149
destruction 157
criture dans un flux 742
et variables objet 118
tat 261
vnement 326
externes 262
File 535
Graphics 295
Image 288
KeyStroke, associer aux actions
372
modle-vue-contrleur, interaction 389
non-objets 196
srialiss 569
sauvegarde 746
variables 117
Onglets dans conteneur 481
Oprateurs
arithmtiques 54
binaires 57
boolens 56
dincrmentation 56
de dcalage 58
de dcrmentation 56
hirarchie 60
instanceof 189, 249

new 131
relationnels 56
surcharge 93
Oppos
composant 367
fentre 367
Ordre de tabulation Voir Focalisation
org.omg.CORBA, package 217
Oriente objet Voir Programmation
outer 263, 331
outerEXouter 262
Outils
de ligne de commande 29
plug-in 14
OutputStream, classe 694, 696
OutputStreamWriter, classe 709
OverlappingFileLockException,
exception 781
OverlayLayout, gestionnaire de
mise en forme 481

P
pack200, compression 590
Package
accs 163
ajout dune classe 160
applications 591
arborescence 163
arithmtique 47
classes publiques/non publiques
164
commentaires javadoc 171
courant 164
dextension 286
de rflexion 236
disponible 68
hirarchie 157
import 158
localisation des classes 159
look and feel 338
nom 157, 170
par dfaut 160
plombage 167
prfixe 158
rpertoires 160
sealing Voir Package, plombage

scurit 167
sous-packages 158
standard 157
utilisation 158
verrouiller 597
visibilit 166
Voir aussi nom du package
Page Web pour excuter des
programmes Java 20
paintComponent, mthode 295,
296, 298, 346, 514
PaintEvent, classe 346
Paires cl/valeur 611
Panneaux
ajout dun bouton 329
conteneurs 396
cration 296
Paquets Voir Package
ParameterizedType, classe 824
Paramtres
de ligne de commande 98
de type 214, 794
primitif 146
des mthodes 46, 143, 217
explicites 133
et implicites 132
implicites 133, 276
nom des 152
nombre variable 218
rgionaux 411
ParseException 417
parseInt, mthode 218
parseInt, mthodeEXparseInt,
mthode 218
Pascal
langage 110
UCSD 11
PasswordChooser, classe 528
Pattern
callback 257
classe 790
Persistance 750
PersistenceService 602
classe 610
Piles, traces 639
Pisteur de mdia 320, 333
play, mthode dURL 578
Plug-in Java 14

Livre Java .book Page 851 Jeudi, 25. novembre 2004 3:04 15

Index

panneau de contrle 599


Poids, champs 488
Point chaud 359
Point2D, classe 301
Pointeurs
C++ 119
de mthodes 236
dlgu 236
Java 10
Points de code 50, 62
caractres complmentaires 50
Polices Voir Fontes
Polymorphisme 183, 187, 242
POO 750
Voir Programmation oriente
objet
Portabilit de Java 12, 55
Porte
dun bloc 78
de classe interne 265
Positionner un cadre 288
pow, mthode 58, 139
Preferences
API 617
classe 622
rfrentiel 618
preferredLayoutSize, mthode
510
Primitif Voir Types
Principe de substitution 183
print, mthode 46, 719
printf
conversions 74
date et heure 75
drapeaux 75
mthode 73
println, mthode 46, 278, 719
printStackTrace, mthode 222
PrintStream, classe 203
PrintWriter 701, 704
classe 719
constructeur 719
private, mot cl 131, 136
Priv(e)s Voir Mthodes, Classes,
Champs ou Donnes
Programmation
des applets 281

vnementielle 326
oriente objet 9, 110
encapsulation Voir Encapsulation
hritage Voir Hritage
polymorphisme Voir Polymorphisme
vocabulaire 111
procdurale 115
Programmation gnrique
dfinition 794
et transtypage 794
exceptions 806
machine virtuelle 800
tableaux 807
Programmes
excuter dans une page Web 20
terminer 341
Properties
classe 614
jeu 615
PropertyChangeEvent, classe 384
PropertyChangeListener, classe
384
Proprits
des classes proxy 279
des interfaces 249
gnriques 250
mthode equals 198
systme 615
protected, mot cl 195, 241
Protg, accs aux classes 195
Proxy 243, 275
classe 276
mthodes 280
public, mot cl 44, 130, 131, 253
PushbackInputStream
classe 703
constructeur 703
putConstraint, mthode 498
putNextEntry, mthode 727
putValue, mthode 369, 376

Q
QuickSort 99
Quitter
un programme 341

851

une boucle 90

R
Raccourcis clavier 467
RaisedBevel, style de bordure 438
raiseSalary, mthode 133
Ramasse-miettes 119, 157, 209
RandomAccessFile
classe 707-708, 736, 776
constructeur 708
Rappel, fonctions 239
Readable, classe 699
reader
objet 727
classe abstraite 709
readExternal, mthode 759
readLine, mthode 732
readObject, mthode 746
ReadOnlyBufferException,
exception 773
readResolve, mthode 761
Recherche
derreurs 30
dichotomique 278
Rectangle englobant 303, 315
Rectangle2D, classe 300
RectangularShape, classe 302
Rels, nombres Voir Virgule flottante
Rfrence externe, syntaxe 264
Rfrences dobjets 750, 756
Rflecteur 219
Rflexion 175, 228, 275
bibliothque 219
crer un tableau gnrique 232
tableaux gnriques 808
Registre des jeux de caractres
IANA 709
Relationnels, oprateurs 56
Relations entre les classes 113
remove, mthode 462
removeAllItems, mthode 443,
445
removeItem, mthode 443, 445
removeItemAt, mthode 443
removeLayoutComponent,
mthode 510

Livre Java .book Page 852 Jeudi, 25. novembre 2004 3:04 15

852

Index

removeUpdate, mthode 410


Remplacement, chane 790
Remplissage
formes 309
tableaux 211
renameTo, mthode 771
repaint, mthode 296
Rpertoires
classes 163
de compilation 128
de Java 27
de travail 535
installation par dfaut 24
javadoc 168
package 160
slection par lutilisateur 536
replace, mthode 67
requestFocus, mthode 367
Rserves dinterface 482
reset, mthode 696
Rsolution de surcharge 149
Ressorts, attachement 499
Ressources 593
nom relatif 595
Retours covariants 185
Rutilisation
botes de dialogue 535
classes 116
revalidate, mthode 402
RIGHT, constante 404
de FlowLayout 393
RigidArea 482
RMI 275
Robot, classe 684
RuntimeException
classe 641
exception 630

S
Saisie
des donnes 70
entiers 411
Scanner, classe 70, 72
SDK 5, 163
Scurit 10, 20, 195, 228, 276
applets certifies 563
classes internes 265
exceptions 616

hritage 241
Java Web Start 600
mcanismes 564
messages davertissement 565
packages 167
seek, mthode 708
Squence de tabulation 511
Srialisation 569, 756
format de stockage 746
mcanisme 761
modifier 758
outil de clonage 763
singletons 760
Serializable, interface 743
ServiceManager, classe 609
ServletException, exception 635
set, mthode 122, 211
setAccelerator, mthode 468, 469
setAccessible, mthode 229, 232
setAction, mthode 462
setActionCommand, mthode
435, 437
setBackground, mthode 307, 309
setBorder, mthode 441
setBounds, mthode 289, 292
setColor, mthode 307, 309, 552
setColumns, mthode 402, 427,
430
setCrc, mthode 728
setCurrentDirectory, mthode
535
setCursor, mthode 364
setDefaultCloseOperation,
mthode 525
setEchoChar, mthode 410
setEditable, mthode 402, 442,
445
setEnabled, mthode 292, 376,
469
setFileFilter, mthode 537
setFileSelectionMode, mthode
536
setFileView, mthode 538, 539
setFirst, mthode 811
setFocusable, mthode 353
setFont, mthode 318, 403
setForeground, mthode 309
setFormatter, mthode 658

setGregorianChange, mthode
126
setHorizontalTextPosition,
mthode 463, 464
setIcon, mthode 405
setIconImage, mthode 288, 293
setJMenuBar, mthode 460, 462
setLabelTable, mthode 447
setLastModified, mthode 771
setLayout, mthode 393
setLevel, mthode 728
setLineWrap, mthode 430
setLocation, mthode 288, 292
setMethod, mthode 728
setMnemonic, mthode 467, 469
setMultiSelectionEnabled,
mthode 536
setNextFocusableComponent,
mthode 512
setPaintLabels, mthode 447
setPaintTicks, mthode 447
setReadOnly, mthode 771
setRect, mthode 301
setResizable, mthode 288, 293
setRows, mthode 427, 430
setSelected, mthode 431, 433,
465
setSelectedFile, mthode 535, 545
setSize, mthode 293
setSnapToTicks, mthode 451
setTabSize, mthode 430
setText, mthode 402, 405
setTime, mthode 122
setTitle, mthode 288, 293
setToolTipText, mthode 476
setUser, mthode 528
setValue, mthode 453
setVisible(true) 528
setVisible, mthode 292, 528
setWrapStyleWord, mthode 430
SHA, algorithme 747
Shallow Voir Clonage superficiel
Shape
classe 302
interface 299
show, mthode 287, 290, 466
showConfirmMessage, mthode
514

Livre Java .book Page 853 Jeudi, 25. novembre 2004 3:04 15

Index

showDocument, mthode 580


showInputDialog, mthode 514
showMessageDialog, mthode
514
showOpenDialog, mthode 535,
536
showOptionDialog, mthode 514
showSaveDialog, mthode 535,
536
showStatus, mthode 580
Signature dune mthode 149,
185
SimpleDateFormat, classe 459
Singletons 760
Sites Web Voir Web, adresses
size, mthode 209-210
skip, mthode 695
SoftBevelBorder
classe 438, 441
constructeur 441
Solaris 25
sort, mthode 99, 101, 245
Sources
des vnements 326
implmenter 380
Souris
vnements 350, 356
nombre de clics 357
Sous-chanes 63
Sous-classes
concrtes 300
DataInputStream 698
DataOutputStream 698
mthodes 177, 185, 188, 290
symtrie et galit 199
SpinnerDateModel, classe 453,
458
SpinnerListModel, classe 458
SpinnerNumberModel, classe
458
Spring, classe 505
Spring.constant, mthode 498
SpringLayout 496-497
classe 504
sqrt, mthode 58
src, rpertoire 27
StackTraceElement, classe 641

start, mthode dapplet 562


startsWith, mthode 67
static, mot cl 139, 154
Statiques Voir Champs, Mthodes, Constantes ou Variables
stop, mthode dapplet 562
strictfp, mot cl 55
StrictMath, classe 59
String, classe 62, 64, 66
StringBuffer, classe 735, 736
StringBuilder, classe 64, 735, 736
StringTokenizer
classe 427, 730, 731
constructeur 731
stringToValue, mthode 418
Strut 482
Stub 275
Substitution, principe de 242
substring, mthode 65, 67
super, mot cl 178
super.paintComponent, mthode
297
Superclasse 176, 183, 229
abstraite 191
mthodes 177
Object 196
Suppression
lments de menu 469
espaces en-tte/fin de chane
403
options dans une liste 443
partie dcimale dun rel 188
Surcharge
de mthodes 149
rsolution de 149
swap, mthode 145
swapHelper, mthode gnrique
815
Swing 282-283, 338, 479
bibliothque 385
composants 540
package 286, 369
panneau 353
squence de tabulation 511

853

swing.defaultlaf, proprit 338


SwingConstants, interface 250,
404
SwingUtilities, classe 534
SwingUtilities.updateComponentTreeUI, mthode 338
switch, instruction 88
Syntaxe
classes internes 261
de Java 9
mthodes 46
rfrence externe 264
System, classe 73, 101, 617
System.exit, mthode 72, 342
System.out, constante 138
System.runFinalizersOnExit,
mthode 157
SystemColor, classe 307
Systme, proprits 615

T
Tableaux 94
anonymes 96
autoboxing 215
C++ 98
copie 97
dclaration 95
dynamiques 232
gnriques 232
rflexion 808
initialisation 96
irrguliers 104
liste de Voir Listes
longueur 97
multidimensionnels 102
paramtres variables 219
programmation gnrique 807
remplacement dlment 211
remplissage 211
taille 207
augmenter 232
transtypage 233
tri 99, 246
Tables de mthodes 186
Tabulation, squence 511

Livre Java .book Page 854 Jeudi, 25. novembre 2004 3:04 15

854

Index

Taille
botes de dialogue 524
cadres 288
par dfaut 290
de lcran 290
fonte 313
tableaux 207
augmenter 232
Tampons
caractristiques 778
structure des donnes 778
Tlchargement, dure pour une
applet 590
Termes cls Java 8
Test, dgalit des chanes 65
Texte
affichage 296
entre 401, 720
mise en forme 311, 405
sortie 718
TextPad 28
this
mot cl 133, 152, 179, 265
pointeur C++ 153
Throwable, classe 222, 633, 641
throws, clause 627
Timer, classe 257, 259
toArray, mthode 211
toBack, mthode 293
toFront, mthode 293
toLowerCase, mthode 67
Toolkit 260
classe 293, 360, 364
toString, mthode 117, 202, 207,
217, 280, 416, 514
gnrique 229
Touches
associer une action 371
code
constantes 351
vrifier 351
virtuel 351, 355
daction 355
de commande 352
de modification 356
dsactiver 373
tat 352
vnements 350, 353

frappes 365
raccourcis clavier 468
toUpperCase, mthode 67
toURL, mthode 771
Trace 277
de piles 639
translatePoint, mthode 364
Transtypage 60, 185, 188, 299
exception 233
tableaux 233
TranstypageEXTranstypage 188
Tri de tableaux 99, 246
trim, mthode 68, 403
trimToSize, mthode 209, 210
TrueType 313
try, exception 222
Type
brut 800
caractre 50
de donnes 47
de retour
covariants 804
effacer 802
de variables 52
double 55
effacer 802, 808
entiers 48
numr 61, 239
vnements de bas niveau 350
gnrique 244
instancier 796, 807
holder 217
identification lexcution 220
interface 820
joker 810
numriques, conversion 59
paramtres 794
primitifs 146, 806
conversion 215
virgule flottante 49
TypeVariable, classe 824

U
UIManager.setLookAndFeel,
mthode 338
UML (Unified Modeling
Language) 114

Unchecked exceptions Voir


Exceptions non vrifies
Unicode 51-52, 314, 352, 694, 709
conversion en octets 718
Units de code 50, 62
caractres complmentaires 50
UNIX 266
make 130
unread, mthode 704
URL 576
absolue 576
classe 577
constructeur 577
UTC (Coordinated Universal
Time) 120
Utilitaires
JAR Voir JAR
javadoc Voir javadoc
rmic 275
WinZip 27

V
Validation de saisie 410
Validit
saisie de texte 412
vrificateurs 414
valueOf, mthode 92, 218
valueToString,mthode 417
Variables 52
caractres $ dans nom 267
copie 251
de type 796
effacer 801
dclaration 47
final 270
homonymes 78
initialisation 53
limites 798
locales 132, 150
accs 269
et champs dinstance 133
final 269
objet 117, 119
porte 78
statiques 269
tableaux 97
Vrificateurs 414

Livre Java .book Page 855 Jeudi, 25. novembre 2004 3:04 15

Index

Verrouillage
fichiers 780
packages 597
Virgule flottante 55
types 49
Visibilit 196
classes 262
locales 268
dans un package 166
mthodes 186, 245
verrouiller les packages 597
void, mot cl 46
Vue
de composant dinterface 386
de fichier 539

W
waitForAll, mthode 323
waitForID, mthode 323
Web, adresses
algorithmes fdlibm 59
codes source 26
didacticiel Java 32
Eclipse 32
exemples de programmes 26
GNU Emacs 35
index API 69
javadoc 172

livre blanc de Java 8


programme errout 32
site de louvrage 2
WinZip 27
while, instruction 82
width 290
attribut dapplet 567
WildcardType>, classe 824
Window, classe 287, 288, 293, 368
Window.show, mthode 287
WindowAdapter, classe 342
WindowClosing
vnement 468
mthode 341-342
WindowEvent
classe 344, 347, 368
vnement 341
WindowListener
classe 344
interface 341
Windows 25, 365
gestion de mise en forme 478
nmake 130
WindowStateListener, classe 344
WinZip, tlchargement 27
Wirth, Niklaus 11, 110
writeChars, mthode 705
writeDouble, mthode 704
writeInt, mthode 704

855

writeObject, mthode 746


Writer, classe abstraite 709

X
X Window 287, 365
X11 295
XEmacs 35
XML 18

Z
ZipEntry
classe 721, 728
constructeur 728
mthode 727
ZipException, exception 726
ZipFile
classe 728
constructeur 728
ZipInputStream 699
classe 721, 727
constructeur 727
ZipOutputStream 699
classe 726-727
constructeur 727
Zones de texte 401, 426
retour automatique la ligne
430

Livre Java .book Page 856 Jeudi, 25. novembre 2004 3:04 15

Vous aimerez peut-être aussi