Vous êtes sur la page 1sur 835

Au cur de Java

volume 1 Notions fondamentales


8e dition Cay S. Horstmann et Gary Cornell

Le prsent ouvrage est la traduction de Core Java, vol. I Fundamentals, 8e d., de Cay Hortsmann et Gary Cornell, publi par Pearson Education Inc./Prentice Hall, Copyright 2008 Sun Microsystem Inc. Authorized translation from the English language edition, entitled CORE JAVA, VOL. I FUNDAMENTALS, 8th Edition By CAY HORTSMANN and GARY CORNELL, published by Pearson Education Inc., publishing as Prentice Hall, Copyright 2008 Sun Microsystem Inc., 4150 Network Circle, Santa Clara, California 95054 USA. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, Inc. French language edition published by PEARSON EDUCATION FRANCE, Copyright 2008

Publi par Pearson Education France 47 bis, rue des Vinaigriers 75010 PARIS Tl. : 01 72 74 90 00 Mise en pages : TyPAO

Titre original : Core Java, volume 1 Fundamentals Traduit de lamricain par : Christiane Silhol et Nathalie Le Guillou de Penanros ISBN original : 0-13-235476-4 Copyright 2008 Sun Microsystems, Inc. Tous droits rservs

ISBN : 978-2-7440-4080-1 Copyright 2009 Pearson Education France Sun Microsystems Inc. 901 San Antonio Road, Palo Alto, California 94303 USA

Tous droits rservs. Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle L. 122-5 2 et 3 a) du code de la proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans le respect des modalits prvues larticle L. 122-10 dudit code.

Table des matires

Introduction .................................................................................................................................. Avertissement au lecteur .......................................................................................................... A propos de ce livre ................................................................................................................. Conventions .............................................................................................................................. Exemples de code ..................................................................................................................... Chapitre 1. Une introduction Java ......................................................................................... Java, plate-forme de programmation ........................................................................................ Les termes cls du livre blanc de Java ...................................................................................... Simplicit ............................................................................................................................ Orient objet ....................................................................................................................... Compatible avec les rseaux ............................................................................................... Fiabilit ............................................................................................................................... Scurit ............................................................................................................................... Architecture neutre ............................................................................................................. Portabilit ........................................................................................................................... Interprt ............................................................................................................................. Performances leves .......................................................................................................... Multithread ......................................................................................................................... Java, langage dynamique .................................................................................................... Les applets Java et Internet ...................................................................................................... Bref historique de Java ............................................................................................................. Les ides fausses les plus rpandues concernant Java .............................................................. Chapitre 2. Lenvironnement de programmation de Java ...................................................... Installation du kit de dveloppement Java ................................................................................ Tlcharger le JDK ............................................................................................................. Congurer le chemin dexcution ...................................................................................... Installer la bibliothque et la documentation ...................................................................... Installer les exemples de programmes ................................................................................ Explorer les rpertoires de Java ..........................................................................................

1 1 2 4 5 7 7 8 8 9 10 10 10 11 12 12 12 13 13 14 15 17 21 21 22 24 26 26 27

IV

Table des matires

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 ................................................................................... Excution dune application graphique .................................................................................... Elaboration et excution dapplets ........................................................................................... Chapitre 3. Structures fondamentales de la programmation Java ......................................... Un exemple simple de programme Java ................................................................................... Commentaires .......................................................................................................................... Types de donnes ..................................................................................................................... Entiers ................................................................................................................................. Types virgule ottante ...................................................................................................... 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 ..................................................................................................................................... Sous-chanes ....................................................................................................................... Concatnation ..................................................................................................................... Les chanes sont inaltrables .............................................................................................. Test dgalit des chanes ................................................................................................... Points de code et units de code ......................................................................................... LAPI String .................................................................................................................... Lire la documentation API en ligne .................................................................................... Construction de chanes ...................................................................................................... Entres et sorties ....................................................................................................................... Lire les caractres entrs ..................................................................................................... Mise en forme de lafchage .............................................................................................. Entre et sortie de chiers ..................................................................................................

28 28 30 31 34 35 37 41 42 45 45 46 47 48 49 50 51 52 52 54 54 55 56 57 58 58 59 60 60 60 61 62 63 64 66 69 70 70 72 76

Table des matires

Flux dexcution ....................................................................................................................... Porte dun bloc .................................................................................................................. Instructions conditionnelles ................................................................................................ Boucles .............................................................................................................................. Boucles dtermines ........................................................................................................... Slections multiples linstruction switch .................................................................... Interrompre le ux 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 ............................................................................................................ Chapitre 4. Objets et classes ...................................................................................................... Introduction la programmation oriente objet ....................................................................... Les classes .......................................................................................................................... Les objets ............................................................................................................................ Identication des classes .......................................................................................................... Relations entre les classes ................................................................................................... Utilisation des classes existantes .............................................................................................. Objets et variables objet ..................................................................................................... La classe GregorianCalendar de la bibliothque Java ..................................................... Les mthodes daltration et les mthodes daccs ............................................................ Dnition de vos propres classes ............................................................................................. Une classe Employee .......................................................................................................... Travailler avec plusieurs chiers 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 .................................................................................................. Champs et mthodes statiques .................................................................................................. Champs statiques ................................................................................................................ Constantes statiques ............................................................................................................ Mthodes statiques .............................................................................................................

78 78 79 82 86 90 92 94 96 97 98 99 101 102 105 108 111 112 113 113 114 115 116 117 120 121 128 128 131 132 132 134 135 137 137 138 138 138 139 140

VI

Table des matires

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 nalize ......................................................................... Packages ................................................................................................................................... Importation des classes ....................................................................................................... Imports statiques ................................................................................................................. Ajout dune classe dans un package ................................................................................... Visibilit dans un package .................................................................................................. Le chemin de classe .................................................................................................................. Dnition du chemin de classe ........................................................................................... 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 .................................................................................... 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 ..................................................................................................................... Object : la superclasse cosmique .............................................................................................. La mthode equals ........................................................................................................... Test dgalit et hritage .....................................................................................................

141 141 144 150 150 151 151 152 153 153 154 158 158 159 161 161 164 165 168 168 169 169 170 171 171 172 172 173 177 178 184 185 186 189 190 192 197 198 198 199

Table des matires

VII

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 types ................................................. Enveloppes dobjets et autoboxing ........................................................................................... Mthodes ayant un nombre variable de paramtres ........................................................... Classes dnumration .............................................................................................................. Rexion .................................................................................................................................. La classe Class ................................................................................................................. Introduction linterception dexceptions .......................................................................... La rexion pour analyser les caractristiques dune classe .............................................. La rexion pour lanalyse des objets lexcution .......................................................... La rexion pour crer un tableau gnrique ..................................................................... Les pointeurs de mthodes ................................................................................................. Conseils pour lutilisation de lhritage ................................................................................... 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 ...................................................................................................... Accs aux variables final partir de mthodes externes ................................................. Classes internes anonymes ................................................................................................. Classes internes statiques .................................................................................................... Proxies ...................................................................................................................................... Proprits des classes proxy ............................................................................................... Chapitre 7. Programmation graphique .................................................................................... Introduction Swing ................................................................................................................ Cration dun cadre .................................................................................................................. Positionnement dun cadre ....................................................................................................... Proprits des cadres .......................................................................................................... Dterminer une taille de cadre adquate ............................................................................. Afchage des informations dans un composant .......................................................................

202 204 210 212 216 217 220 221 223 224 226 227 232 237 240 244 247 248 253 254 255 261 264 265 268 269 271 272 274 276 280 284 287 288 291 294 296 296 300

VIII

Table des matires

Formes 2D ................................................................................................................................ Couleurs ................................................................................................................................... Texte et polices ......................................................................................................................... Afchage dimages .................................................................................................................. Chapitre 8. Gestion des vnements .......................................................................................... Introduction la gestion des vnements ................................................................................. Exemple : gestion dun clic de bouton ............................................................................... Etre confortable avec les classes internes ........................................................................... Crer des couteurs contenant un seul appel de mthode .................................................. Exemple : modication du "look and feel" ......................................................................... Classes adaptateurs ............................................................................................................. Actions ..................................................................................................................................... Evnements de la souris ..................................................................................................... Hirarchie des vnements AWT ............................................................................................. Evnements smantiques et de bas niveau ......................................................................... Chapitre 9. Swing et les composants dinterface utilisateur ................................................... Swing et larchitecture Modle-Vue-Contrleur ...................................................................... Modles de conception ....................................................................................................... Larchitecture Modle-Vue-Contrleur .............................................................................. Une analyse Modle-Vue-Contrleur des boutons Swing .................................................. Introduction la gestion de mise en forme .............................................................................. Gestionnaire BorderLayout ................................................................................................ Disposition des grilles ........................................................................................................ Entre de texte .......................................................................................................................... Champs de texte .................................................................................................................. Etiquettes et composants dtiquetage ................................................................................ Champs de mot de passe ..................................................................................................... Zones de texte ..................................................................................................................... Volets de dlement ............................................................................................................ Composants du choix ............................................................................................................... Cases cocher .................................................................................................................... Boutons radio ...................................................................................................................... Bordures ............................................................................................................................. Listes droulantes ............................................................................................................... Curseurs .............................................................................................................................. Menus ....................................................................................................................................... Cration dun menu ............................................................................................................ Icnes et options de menu ..................................................................................................

305 312 315 323 327 327 330 334 337 337 341 344 352 359 361 365 366 366 367 370 372 374 376 380 380 382 383 384 385 387 387 390 394 398 402 408 408 410

Table des matires

IX

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 GridBagLayout .............................................................................................. GroupLayout ....................................................................................................................... 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 .......................................................................................................... Chapitre 10. Dployer des applets et des applications ............................................................. Les chiers JAR ....................................................................................................................... Le manifeste ....................................................................................................................... Fichiers JAR excutables .................................................................................................... Les ressources ..................................................................................................................... Verrouillage ........................................................................................................................ Java Web Start .......................................................................................................................... Le bac sable ..................................................................................................................... Code sign .......................................................................................................................... LAPI JNLP ....................................................................................................................... Les applets ................................................................................................................................ Un petit applet .................................................................................................................... Conversion dune application en applet .............................................................................. Balises HTML et attributs pour applets .............................................................................. La balise object ................................................................................................................... Passer des informations un applet avec des paramtres ................................................... Accder aux images et chiers audio ................................................................................. Le contexte dapplet ........................................................................................................... La communication interapplets .......................................................................................... Faire afcher des informations par le navigateur ............................................................... Cest un applet et cest aussi une application ! ...................................................................

411 412 414 416 420 422 425 427 436 445 446 450 451 452 461 466 472 483 491 492 493 494 495 498 498 502 503 505 513 514 517 518 521 521 526 528 528 528 530

Table des matires

Stockage des prfrences dapplications .................................................................................. Concordances de proprits ................................................................................................ LAPI Preferences .............................................................................................................. Chapitre 11. Exceptions, consignation, assertion et mise au point ......................................... 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 nally .................................................................................................................. Analyser les traces de piles ................................................................................................. Quelques conseils sur lutilisation des exceptions ................................................................... Les assertions ........................................................................................................................... Activation et dsactivation des assertions ........................................................................... Vrication des paramtres avec des assertions ................................................................. Utiliser des assertions pour documenter des hypothses .................................................... La consignation ........................................................................................................................ Consignation de base .......................................................................................................... Consignation avance ......................................................................................................... Modier la conguration du gestionnaire de journaux ...................................................... La localisation .................................................................................................................... Les gestionnaires ................................................................................................................ Les ltres ............................................................................................................................ Les formateurs .................................................................................................................... Une astuce de consignation ................................................................................................ Les techniques de mise au point ............................................................................................... Utiliser une fentre de console ........................................................................................... Tracer les vnements AWT ............................................................................................... Le robot AWT ..................................................................................................................... Utiliser un dbogueur ............................................................................................................... Chapitre 12. Programmation gnrique ................................................................................... Pourquoi la programmation gnrique ? .................................................................................. Y a-t-il un programmeur gnrique dans la salle ? ............................................................. Dnition dune classe gnrique simple ................................................................................. Mthodes gnriques ................................................................................................................ Limites pour variables de type .................................................................................................

535 536 540 547 548 549 551 553 554 555 557 557 558 561 564 567 568 568 569 570 571 571 574 575 576 579 579 580 587 593 595 598 602 607 608 609 610 612 613

Table des matires

XI

Code gnrique et machine virtuelle ........................................................................................ Traduire les expressions gnriques ................................................................................... Traduire les mthodes gnriques ...................................................................................... Appeler un code existant .................................................................................................... Restrictions et limites ............................................................................................................... Les paramtres de type ne peuvent pas tre instancis avec les types primitifs ................. Les informations sur le type dexcution ne fonctionnent quavec les types bruts ............ Vous ne pouvez pas lancer ou intercepter des instances dune classe gnrique ............... Les tableaux de types avec paramtres ne sont pas autoriss ............................................. Vous ne pouvez pas instancier des variables de type .......................................................... Les variables de type ne sont pas valables dans des contextes statiques des classes gnriques ........................................................................................................ Attention aux conits aprs un effacement ........................................................................ Rgles dhritage pour les types gnriques ............................................................................ Types joker ............................................................................................................................... Limites de supertypes pour les jokers ................................................................................. Jokers sans limites .............................................................................................................. Capture de caractres joker ................................................................................................. Rexion et gnrique .............................................................................................................. Utilisation des paramtres Class<T> pour la concordance de type ................................... Informations de type gnrique dans la machine virtuelle ................................................. Chapitre 13. Collections ............................................................................................................. Les interfaces de collection ...................................................................................................... Sparer les interfaces dune collection et leur implmentation .......................................... Interfaces de collection et ditration dans la bibliothque Java ........................................ Suppression dlments ...................................................................................................... Mthodes utilitaires gnriques .......................................................................................... Les collections concrtes .......................................................................................................... Listes chanes .................................................................................................................... Listes de tableaux ............................................................................................................... Tables de hachage ............................................................................................................... Arbres ................................................................................................................................. Queues et deques ................................................................................................................ Queues de priorit ............................................................................................................... Cartes .................................................................................................................................. Classes de cartes et de set spcialises ............................................................................... La structure des collections ...................................................................................................... Les vues et les emballages .................................................................................................. Les oprations de masse ..................................................................................................... Conversion entre collections et tableaux ............................................................................

615 616 617 619 620 620 620 621 622 622 624 624 625 627 628 630 631 634 635 636 643 643 644 646 649 649 651 652 660 661 664 670 671 673 677 681 685 691 692

XII

Table des matires

Algorithmes .............................................................................................................................. Trier et mlanger ................................................................................................................. Recherche binaire ............................................................................................................... Algorithmes simples ........................................................................................................... Ecrire vos propres algorithmes ........................................................................................... Les anciennes collections ......................................................................................................... La classe Hashtable ............................................................................................................ Les numrations ................................................................................................................ Cartes de proprits ............................................................................................................ Piles .................................................................................................................................... Les ensembles de bits ......................................................................................................... Chapitre 14. Multithreads .......................................................................................................... Quest-ce quun thread ? .......................................................................................................... Utiliser des threads pour laisser une chance aux autres tches ........................................... Interrompre des threads ............................................................................................................ Les tats dun thread ........................................................................................................... Threads termins ................................................................................................................. Proprits dun thread .............................................................................................................. Priorits dun thread ........................................................................................................... Threads dmons .................................................................................................................. Gestionnaire dexceptions non rcupres ......................................................................... Synchronisation ........................................................................................................................ Exemple de condition de course ......................................................................................... Explication des conditions de course ................................................................................. Verrous dobjet ................................................................................................................... Objets de condition ............................................................................................................. Le mot cl synchronized ..................................................................................................... Blocs synchroniss ............................................................................................................. Le concept des moniteurs ................................................................................................... Champs volatiles ................................................................................................................. Verrous morts ...................................................................................................................... Test de verrous et temporisations ....................................................................................... Lire et crire des verrous .................................................................................................... Pourquoi les mthodes stop et suspend ne sont plus utilises .................................................. Queues de blocage .................................................................................................................... Collections compatibles avec les threads ................................................................................. Cartes, jeux et queues efcaces .......................................................................................... CopyOnWriteArray ........................................................................................................... Anciennes collections compatibles avec les threads .......................................................... Callable et Future .....................................................................................................................

693 694 696 698 699 700 700 701 702 702 703 707 708 713 718 721 722 724 724 725 725 727 727 731 732 735 740 743 745 745 747 750 751 752 754 760 761 762 763 764

Table des matires

XIII

Executors .................................................................................................................................. Pools de threads .................................................................................................................. Excution programme ....................................................................................................... Contrle de groupes de tches ............................................................................................ Synchronizers ........................................................................................................................... Smaphores ......................................................................................................................... Verrous Countdown ............................................................................................................ Barrires ............................................................................................................................. Exchanger ........................................................................................................................... Queues synchrones ............................................................................................................. Exemple : pause et reprise dune animation ....................................................................... Threads et Swing ................................................................................................................ Excution de tches longues ............................................................................................... Utilisation du travailleur Swing .......................................................................................... La rgle du thread unique ................................................................................................... Annexe. Les mots cls de Java ................................................................................................... Index ..............................................................................................................................................

768 769 772 773 774 775 776 776 777 777 777 783 784 788 795 797 801

Introduction
Avertissement au lecteur
Vers la n 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 d. 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 conance 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 sept rvisions majeures du kit de dveloppement Java. Au cours des onze dernires annes, lAPI (interface de programmation dapplication) est passe de 200 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. Louvrage que vous tenez entre les mains est le premier volume de la huitime dition de Au cur de Java. 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. Cette dition a t mise jour pour traiter des nouveauts de Java Standard Edition (SE) 6. 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 autre que Java et quil fuit les ouvrages qui fourmillent dexemples dpourvus dutilit pratique (comme les grille-pain, les animaux du zoo ou le "texte nerveux"). Vous nen trouverez pas ici. Notre objectif est de vous permettre dapprhender le langage et la bibliothque Java, et non de vous donner lillusion que vous les comprenez. Vous trouverez de nombreux programmes de dmonstration qui abordent la plupart des sujets traits dans cet ouvrage. Ces programmes sont volontairement simples an que le lecteur puisse se concentrer 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 m m

la programmation oriente objet ; le mcanisme de rexion de Java et de proxy ; les interfaces et classes internes ;

Au cur de Java 2 - Notions fondamentales

m m m m m m

le modle dcouteur dvnement ; la conception dinterfaces graphiques avec la bote outils Swing ; la gestion des exceptions ; la programmation gnrique ; le cadre des collections ; la simultanit.

Enn, 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 main, 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 m m m m m m m m m m

les chiers et les ux ; les objets distribus ; les bases de donnes ; les composants GUI avancs ; les mthodes natives ; le traitement XML ; la programmation rseau ; le graphisme avanc ; linternationalisation ; JavaBeans ; les annotations.

Pour cette dition, nous avons rorganis le contenu des deux volumes. Le multithread, notamment, est trait dans le Volume I, car il est devenu trs important, la loi de Moore tant dsormais obsolte. 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://horstmann.com/corejava une liste de questions courantes, de corrections et dexplications. Un formulaire permettant de signaler des bogues et de suggrer des amliorations est plac stratgiquement la n de la page des corrections (pour vous encourager la lire). Ne soyez pas du 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.

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 ns. Nous terminerons par un historique de Java et nous prciserons la manire dont il a volu.

Introduction

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 au 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. La 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 modier 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 proter 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. Au Chapitre 7, nous commencerons vritablement la programmation dapplications. Tout programmeur en Java doit connatre un minimum de programmation de GUI, des bases que vous trouverez dans ce volume. Nous vous montrerons comment crer des fentres, y dessiner et y tracer des gures gomtriques, formater du texte avec diffrentes polices et afcher des images. Le Chapitre 8 sera consacr une tude dtaille du modle dvnement AWT, la bote outils de fentre abstraite. 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. 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 dlement, les zones de listes, les menus et les botes de dialogue. Certains de ces composants, plus avancs, seront tudis dans le Volume II. Le Chapitre 10 indique comment dployer des programmes, sous forme dapplications ou dapplets. Nous expliquons comment emballer des programmes dans les chiers JAR et livrer des applications

Au cur de Java 2 - Notions fondamentales

sur Internet avec Java Web Start et les mcanismes dapplet. Enn, nous verrons la manire dont les programmes Java peuvent stocker et rcuprer des informations de conguration lorsquils ont t dploys. 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 dommages. Les exceptions fournissent un moyen efcace 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 seconde partie de ce chapitre vous donnera quelques astuces de dbogage. Pour terminer, nous vous accompagnerons dans une session de dbogage. Le Chapitre 12 donne une vue densemble de la programmation gnrique, une avance majeure de Java SE 5.0. Elle facilite la lecture de vos programmes, tout en les scurisant. Nous vous montrerons comment utiliser des types forts et comment traiter les complexits lies aux transtypages peu srs, mais aussi comment grer les complexits naissant du besoin de compatibilit avec les anciennes versions de Java. Le Chapitre 13 traitera des cadres de collections de la plate-forme Java. Lorsque vous souhaiterez collecter plusieurs objets pour les rcuprer plus tard, vous utiliserez une collection adapte votre situation, au lieu de simplement faire entrer les lments dans un tableau. Ce chapitre indique comment proter des collections standard dj cres pour vous. Le Chapitre 14 clturera ce livre avec une discussion sur le multithreading, qui permet de programmer des tches raliser en parallle (un thread est un ux de contrle dans un programme). Nous vous indiquerons comment congurer les threads et en grer la synchronisation. Le multithreading a beaucoup volu dans Java SE 5.0 et nous vous en montrerons les nouveaux mcanismes. LAnnexe est consacre aux mots cls du langage Java.

Conventions
Comme cest lusage dans la plupart des ouvrages dinformatique, nous employons la police Courier pour le code des programmes.
INFO 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.

INFO C++
De nombreuses notes dinfo C++ prcisent les diffrences entre Java et C++. Vous pouvez ignorer ces notes si vous ntes pas familiaris avec ce langage.

Introduction

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 n de la section. Ces descriptions sont un peu plus informelles que celles de la documentation ofcielle en ligne, mais nous esprons quelles sont plus instructives. Les programmes dont le code source se trouve sur le Web sont fournis sous forme de listings, comme ceci :
Listing 2.4 : WelcomeApplet.java

Exemples de code
Vous trouverez sur le site des ditions Pearson Education (www.pearsoneducation.fr) 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 listings dexemple.

1
Une introduction Java
Au sommaire de ce chapitre

Java, plate-forme de programmation Les termes cls du livre blanc de Java Les applets 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 Microsystems, 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

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 Orient objet Compatible avec les rseaux Fiabilit Scurit Architecture neutre Portabilit Interprt Performances leves Multithread Dynamique

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 gure ladresse http://java.sun.com/docs/overviews/java/javaoverview-1.html.

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.

Chapitre 1

Une introduction Java

La syntaxe de Java reprsente rellement une version amliore de C++. Les chiers 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 C++ dcrivant plus prcisment les diffrences entre Java et C++). Les concepteurs nont cependant pas tent de modier 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 manuellement, 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 de la bibliothque a atteint des proportions considrables. Il existe maintenant une Java Micro Edition spare, avec une bibliothque plus petite, convenant aux priphriques intgrs.

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 ensuite aux outils utiliss pour la fabriquer. Par opposition, le menuisier "non orient objet" penserait dabord ses outils. Les facilits orientes objet de Java sont essentiellement celles de C++...

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), que nous verrons au Chapitre 5.
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.

10

Au cur de Java 2 - Notions fondamentales

Compatible avec les rseaux


*

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

Nous avons trouv que les fonctionnalits rseau de Java taient la fois ables 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 II de cet ouvrage). Le mcanisme dinvocation de mthode distance (RMI) autorise la communication entre objets distribus (voir galement le Volume II).

Fiabilit
*

Java a t conu pour que les programmes qui lutilisent soient ables sous diffrents aspects. Sa conception encourage le programmeur traquer prventivement les ventuels problmes, lancer des vrications dynamiques en cours dexcution et liminer les situations gnratrices derreurs... La seule et unique grosse diffrence entre C/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 nexploitent 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 chiers. 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 difciles 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 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 de scurit de Java 1.0. Sun Microsystems a encourag la recherche sur la scurit de Java, en

Chapitre 1

Une introduction Java

11

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 difcile 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 m 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 chiers 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 II, ditions CampusPress), qui vous permet de savoir qui la crite. Votre degr de conance 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 sufsant. Tous les utilisateurs des produits Microsoft peuvent conrmer 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 chier 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 trente 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 efcace 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 vrier le comportement des suites dinstructions. Certains programmes vont jusqu produire des bytecodes la vole, en amliorant dynamiquement les capacits dun programme en cours dexcution.

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 spcication. Les tailles des types de donnes primaires sont spcies, 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 shortint et ne peut pas en contenir plus quun longint. Le principe dune taille xe pour les types numriques limine les principaux soucis du portage dapplications. Les donnes binaires sont stockes et transmises dans un format xe, ce qui met n la confusion sur lordre des octets. Les chanes sont enregistres au format Unicode standard.
*

Les bibliothques intgres au systme dnissent 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 lefcacit de linterface utilisateur, et ce sont celles qui ont bnci 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. Aujourdhui, les bytecodes sont traduits en code machine par le compilateur en juste--temps.

Performances leves
*

En rgle gnrale, les performances des bytecodes interprts sont tout fait sufsantes ; il existe toutefois des situations dans lesquelles des performances plus leves sont ncessaires. Les bytecodes peuvent tre traduits la vole (en cours dexcution) en code machine pour lunit centrale destine accueillir lapplication.

Aux premiers temps de Java, de nombreux utilisateurs rejetaient la formule "performances leves". Il existe toutefois aujourdhui des compilateurs JIT (just-in-time, juste--temps) dune qualit telle

Chapitre 1

Une introduction Java

13

quils concurrencent les compilateurs traditionnels et, dans certains cas, les dpassent du fait quils disposent de davantage dinformations. Par exemple, un compilateur JIT est capable didentier du code souvent excut et doptimiser exclusivement la vitesse de celui-ci. Une optimisation plus pousse consiste liminer les appels de fonctions. Le compilateur en juste--temps connat les classes qui ont t charges. Il peut utiliser la suppression lorsque, en fonction de la collection de classes actuellement charge, une fonction particulire nest jamais crase. Il peut ensuite annuler cette optimisation si ncessaire.

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. 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 dinformations 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 enchables et les bases de donnes objet.
INFO
Peu aprs les premiers succs de Java, Microsoft a sorti un produit intitul J++ qui prsente un lien de parent avec Java. 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.

14

Au cur de Java 2 - Notions fondamentales

Les applets 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. Vous naurez besoin dinstaller aucun logiciel. Sun fournit sous licence le code source de Java et afrme quaucun changement naffectera le langage et la structure de la bibliothque de base. Vous obtiendrez la dernire version du programme ds que vous visiterez la page Web contenant lapplet. Et surtout, grce la scurit de la machine virtuelle, vous naurez plus vous inquiter des attaques provenant dun code hostile. 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 dafcher 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). A leur apparition, les applets ont suscit une grande excitation. Beaucoup pensent que lattirance pour les applets a t responsable de la formidable popularit de Java. Or, cette premire excitation sest rapidement transforme en frustration. Diffrentes versions de Netscape et dInternet Explorer excutaient des versions diffrentes de Java, certaines particulirement obsoltes. Cette situation malheureuse a compliqu de plus en plus le dveloppement dapplets capables de tirer parti de la version la plus actuelle de Java. Aujourdhui, la plupart des pages Web utilisent simplement JavaScript ou Flash pour obtenir des effets dynamiques dans le navigateur. Java, quant lui, est devenu le langage le plus populaire pour dvelopper des applications ct serveur qui produisent des pages Web et ralisent la logique en arrire-plan.
Figure 1.1
Lapplet Jmol.

Chapitre 1

Une introduction Java

15

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 dcodeurs du 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 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 n". 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. Malheureusement, personne ne fut intress pour le produire chez Sun. Lquipe de Green dut trouver dautres ouvertures pour commercialiser sa technologie. Toutefois, aucune des grandes socits dlectronique de grande consommation na t intresse. Le groupe soumit alors un nouveau projet. Il proposa la conception dun dcodeur 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 et principal agent marketing, 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

16

Au cur de Java 2 - Notions fondamentales

dune des rares choses dans le courant client/serveur qui ncessitait certaines des actions bizarres quils avaient ralises : larchitecture neutre, le temps rel, la abilit, 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. 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 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.0. Son successeur, la version 1.1, a combl les fosss les plus vidents, a grandement amlior la capacit de rexion 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 signicative (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 d 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. La version 6 (dpourvue du sufxe .0) est sortie n 2006. Une fois de plus, le langage na pas connu de changement, mais les performances et la bibliothque ont t amliores. Le Tableau 1.1 montre lvolution du langage Java et de la bibliothque. Vous le voyez, la taille de linterface de programmation dapplication (API) a considrablement augment.

Chapitre 1

Une introduction Java

17

Tableau 1.1 : Evolution du langage Java

Version 1.0 1.1 1.2 1.3 1.4 5.0

Anne 1996 1997 1998 2000 2004 2004

Nouvelles fonctionnalits du langage Le langage lui-mme Classes internes Aucune Aucune Assertions Classes gnriques, boucle for each, varargs, autoboxing, mtadonnes, numrations, importation static Aucune

Nombre de classes et dinterfaces 211 477 1 524 1 840 2 723 3 279

2006

3 777

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 II 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 difcult 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.

18

Au cur de Java 2 - Notions fondamentales

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 efcaces 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 gurent 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 nal 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. 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. Sun Microsystems distribue Java sous licence aux distributeurs et aux utilisateurs naux. 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 modication et redistribution. Jusqu prsent, Java est en "source ferme, mais fonctionne bien". Cette situation a considrablement chang en 2007, lorsque Sun a annonc la disponibilit des futures versions de Java dans le cadre de la GPL (General Public Licence ou licence publique gnrale), la licence open source de Linux. Reste voir comment Sun grera Java lavenir, mais il ne fait aucun

Chapitre 1

Une introduction Java

19

doute que le ct open source de Java a constitu une volution trs courageuse, qui prolongera la vie de Java de nombreuses annes. 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++ et mme plus rapidement dans certains cas. Java est moins rapide que le C++. Le dmarrage de la machine virtuelle peut tre lent et 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 des applications Java. 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 dnition mme dun applet un programme Java sexcutant dans un navigateur. Mais la plupart des programmes Java sont des applications autonomes qui sexcutent indpendamment dun navigateur Web. De nombreux programmes Java sexcutent en fait sur des serveurs Web et produisent le code des pages Web. 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 spcique. Les chercheurs ont considr ce systme de scurit comme un d 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 chiers 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 de chercheurs 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 chiers 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.

20

Au cur de Java 2 - Notions fondamentales

JavaScript est une version simplie 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. JavaScript est plus troitement intgr dans les navigateurs que ne le sont les applets Java. En particulier, un programme JavaScript peut modier le document afch, tandis quun applet ne peut que contrler lapparence dune zone limite. 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.

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 Excution dune application graphique Elaboration et excution dapplets
Ce chapitre traite de linstallation du kit de dveloppement Java (JDK) 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 peuvent ncessiter des ressources importantes et tre lourds utiliser pour de petits programmes. 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 du kit de dveloppement Java (JDK) 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.

22

Au cur de Java 2 - Notions fondamentales

INFO
Certaines distributions de Linux possdent des versions premballes du JDK. Ainsi, par exemple, sous Ubuntu, vous pouvez installer le JDK en installant simplement le package sun-java6-jdk avec apt-get ou la GUI Synaptic.

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. Voyez le Tableau 2.1 pour en obtenir un rsum. 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. Il existe galement un JRE (Java Runtime Environment) contenant la machine virtuelle, mais pas le compilateur. Or, cela nest pas adapt pour les dveloppeurs, il est plutt destin aux utilisateurs nayant pas besoin du compilateur. Vous rencontrerez aussi trs souvent le terme "Java SE". Il signie "Java Standard Edition", par opposition Java EE (Entreprise Edition) et Java ME (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 Software Development Kit version 5.0, soit J2SE SDK 5.0. Cela peut tre assez dsarmant pour les ingnieurs, mais cest l le ct cach du marketing. Heureusement, en 2006, le bon sens a repris le dessus. Le surnom Java 2, parfaitement inutile, a donc t abandonn, et la version actuelle de Java Standard Edition sest appele Java SE 6. Vous trouverez toujours, par moments, une rfrence aux versions 1.5 et 1.6, ce sont simplement des synonymes des versions 5.0 et 6. Enn, lorsque Sun apporte un changement mineur pour corriger des problmes urgents, il nomme cette opration une mise jour. Ainsi, la premire mise jour du kit de dveloppement pour Java SE 6 sintitule ofciellement JDK 6u1 et porte le numro de version interne 1.6.0_01. Pour les utilisateurs de Solaris, de Linux ou de Windows, accdez ladresse http://java.sun.com/ javase pour tlcharger le JDK. Demandez la version 6 ou suprieure, puis choisissez votre plateforme. Ne vous inquitez pas si le logiciel est quali de "mise jour". Le module contient la version actuelle de lensemble du JDK. 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.

Chapitre 2

Lenvironnement de programmation de Java

23

Tableau 2.1 : Le jargon de Java

Nom Java Development Kit Java Runtime Environment Standard Edition Enterprise Edition Micro Edition Java 2 Software Development Kit Mise jour (Update) NetBeans

Acronyme JDK JRE SE EE ME J2 SDK u

Explication Le logiciel destin aux programmeurs qui souhaitent crire des programmes Java Le logiciel destin aux clients qui veulent excuter des programmes Java La plate-forme Java utiliser sur les ordinateurs de bureau et les applications de serveur simples La plate-forme Java pour les applications de serveur complexes La plate-forme Java utiliser sur les tlphones portables et autres petits appareils Un terme obsolte dsignant les versions de Java entre 1998 et 2006 Terme obsolte dsignant le JDK entre 1998 et 2006 Terme employ par Sun pour dsigner un correctif de bogue Lenvironnement de dveloppement intgr de Sun

Une fois le JDK tlcharg, suivez les instructions dinstallation, qui sont fonction de la plate-forme. A lheure o nous crivons, elles taient disponibles ladresse http://java.sun.com/javase/6/ webnotes/install/index.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 jdk1.6.0. Ceci peut paratre pnible, mais le numro de version est nalement 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\jdk1.6.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/jdk1.6.0/bin ou C:\jdk1.6.0\bin.

24

Au cur de Java 2 - Notions fondamentales

Congurer 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 chiers excutables. Les directives concernant cette tape varient galement en fonction du systme dexploitation.
m

Sous UNIX (y compris Solaris ou Linux), la procdure pour modier 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 n de votre chier ~/.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 n de votre chier ~/.bashrc ou ~/.bash_profile :
export PATH=/usr/local/jdk/bin:$PATH
m

Sous Windows, connectez-vous en tant quadministrateur. Ouvrez le Panneau de conguration, passez en afchage classique et slectionnez Systme. Sous Windows NT/2000/XP, la bote de dialogue des proprits systme souvre immdiatement. Sous Vista, slectionnez les paramtres systme avancs (voir Figure 2.1). Dans la bote de dialogue des proprits du systme, cliquez sur longlet Paramtres systme avancs, puis sur le bouton Variables denvironnement. Parcourez la fentre Variables systme pour rechercher la variable nomme Path (voir Figure 2.2). 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 conguration. Toute nouvelle fentre console que vous lancerez comprendra le chemin correct. Procdez de la faon suivante pour vrier que vous avez effectu les manipulations appropries. Dmarrez une fentre shell. Tapez la ligne :
java -version

Appuyez sur la touche Entre. Vous devez obtenir un afchage comparable ce qui suit :
java version "1.6.0_01" Java(TM) SE Runtime Environment (build 1.6.0_01-b06) Java HotSpot(TM) Client VM (build 1.6.0_01-b06, mixed mode, sharing)

Si, la place, vous obtenez un message du type "java: command not found" ou "The name specied is not recognized as an internal or external command, operable program or batch le", messages qui signalent une commande ou un chier erron, vous devez vrier votre installation.
INFO
Selon la version de Windows, une fentre shell souvre dune des manires suivantes. Sous Windows NT/2000/XP, choisissez loption Excuter du menu Dmarrer et tapez cmd. Sous Vista, cliquez sur Dmarrer et tapez cmd. Appuyez sur Entre pour ouvrir la fentre shell. Si vous navez jamais utilis de fentre shell, nous vous proposons un didacticiel enseignant les bases de la ligne de commande. De nombreux services informatiques proposent des didacticiels sur le Web, notamment ladresse http://www.cs.sjsu.edu/faculty/horstman/CS46A/windows/tutorial.html.

Chapitre 2

Lenvironnement de programmation de Java

25

Figure 2.1
Lancement de la bote de dialogue des proprits du systme sous Windows Vista.

Figure 2.2
Paramtrage de la variable denvironnement Path sous Windows Vista.

26

Au cur de Java 2 - Notions fondamentales

Installer la bibliothque et la documentation


Les chiers source de bibliothque sont fournis dans le JDK sous la forme dun chier 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 gure dans le chemin dexcution. 2. Ouvrez une fentre shell. 3. Positionnez-vous dans le rpertoire jdk (par ex. /usr/local/jdk1.6.0 ou C:\jdk1.6.0). 4. Crez un sous-rpertoire src.
mkdir src cd src

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

(ou jarxvf..\src.zip sous Windows).


ASTUCE
Le chier 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://download.java.net/jdk6.

La documentation est contenue dans un chier compress spar du JDK. Vous pouvez tlcharger la documentation ladresse http://java.sun.com/javase/downloads. Procdez de la faon suivante : 1. Assurez-vous que le JDK est install et que le rpertoire jdk/bin gure dans le chemin dexcution. 2. Copiez le chier zip de documentation dans le rpertoire qui contient le rpertoire jdk. Le chier est nomm jdk-version-doc.zip, o version ressemble 6. 3. Lancez une fentre shell. 4. Positionnez-vous dans le rpertoire jdk. 5. Excutez la commande :
jar xvf jdk-version-doc.zip

o version est le numro de version appropri.

Installer les exemples de programmes


Il est conseill dinstaller les exemples de programmes que vous pouvez tlcharger ladresse www.pearsoneducation.fr. Les programmes sont compresss dans un chier zip corejavavol1.zip. Dcompressez-les dans un rpertoire spar que nous vous recommandons dappeler CoreJavaBook. Procdez de la faon suivante :

Chapitre 2

Lenvironnement de programmation de Java

27

1. Assurez-vous que le JDK est install et que le rpertoire jdk/bin gure dans le chemin dexcution. 2. Crez un rpertoire nomm CoreJavaBook. 3. Copiez le chier corejavavol1.zip dans ce rpertoire. 4. Lancez une fentre shell. 5. Positionnez-vous dans le rpertoire CoreJavaBook. 6. Excutez la commande :
jar xvf corejavavol1.zip

Explorer les rpertoires de Java


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

Structure du rpertoire jdk bin demo docs include jre lib src

Description Le nom peut tre diffrent, par exemple jdk5.0 Compilateur et outils Dmos Documentation de la bibliothque au format HTML (aprs dcompression de j2sdkversion-doc.zip) Fichiers pour les mthodes natives (voir Volume II) Fichiers denvironnement dexcution de Java Fichiers de bibliothque 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
Dnissez un signet dans votre navigateur pour le chier docs/api/index.html. Vous consulterez frquemment cette page au cours de votre tude de la plate-forme Java !

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. Mme si cela peut paratre lourd, il sagit dune comptence essentielle. A la premire installation de Java, vous devrez mettre au propre votre installation avant dinstaller un environnement de dveloppement. De plus, en ralisant vous-mme les tapes de base, vous apprhenderez mieux ce que fait lenvironnement de dveloppement dans votre dos. Toutefois, lorsque vous aurez matris les bases de la compilation et de lexcution des programmes Java, vous aurez besoin dun environnement de dveloppement professionnel. Au cours de la dernire dcennie, ces environnements sont devenus si puissants et si commodes quil nest tout simplement pas logique de sen passer. Deux excellents choix sont les programmes gratuits Eclipse et NetBeans. Nous vous prsentons dans ce chapitre une mise en route dEclipse, qui est un peu plus performant que NetBeans, mme si ce dernier rattrape rapidement son retard. Bien entendu, si vous disposez dj dun environnement de dveloppement, nhsitez pas lutiliser. Auparavant, nous recommandions dutiliser un diteur de texte comme Emacs, JEdit ou TextPad pour des programmes simples. Nous ne le faisons plus car les environnements de dveloppement intgrs sont aujourdhui rapides et pratiques. En rsum, nous supposons que vous savez utiliser les outils de base du JDK et que vous devriez vous familiariser avec un environnement de dveloppement intgr.

Utilisation des outils de ligne de commande


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

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

Chapitre 2

Lenvironnement de programmation de Java

29

Figure 2.3
Compilation et excution de Welcome.java.

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 chier Welcome.java en un chier Welcome.class. Le programme java lance la machine virtuelle Java. Il interprte les bytecodes que le compilateur a placs dans le chier class.
INFO
Si vous obtenez un message derreur relatif la ligne
for (String g: greeting)

cela signie que vous utilisez probablement une ancienne version du compilateur Java. Java SE 5.0 a introduit plusieurs fonctions utiles au langage de programmation Java dont nous proterons 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]);

Le programme Welcome est extrmement simple. Il se contente dafcher un message sur lcran. Vous pouvez examiner ce programme dans le Listing 2.1 nous en expliquerons le fonctionnement dans le prochain chapitre.
Listing 2.1 : Welcome.java
/** * Ce programme affiche un message de bienvenue des auteurs. * @version 1.20 2004-02-28 * @author Cay Horstmann */

30

Au cur de Java 2 - Notions fondamentales

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. Le compilateur requiert un nom de chier Welcome.java. Lorsque vous excutez le programme, vous spciez un nom de classe (Welcome) sans extension .java ni .class. Si vous obtenez un message tel que "Bad command or le name" ou "javac: command not found", signalant une commande errone, vous devez vrier votre installation, en particulier la conguration du chemin dexcution. Si javac signale une erreur "cannot read: Welcome.java", signalant une erreur de lecture du chier, vriez si ce chier est prsent dans le rpertoire. Sous UNIX, vriez 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 chier. Si vous utilisez le Bloc-notes pour modier Welcome.java, le chier sera enregistr sous le nom Welcome.java.txt. Dans la conguration par dfaut de Windows, lExplorateur conspire avec le Bloc-notes et masque lextension .txt, car elle est considre comme un "type de chier connu". Dans ce cas, vous devez renommer le chier laide de la commande shell ren ou le renregistrer, en plaant des guillemets autour du nom de chier : "Welcome.java".

Si java afche un message signalant une erreur "java.lang.NoClassDefFoundError", vriez soigneusement le nom de la classe concerne. Si linterprteur se plaint que welcome contient un w minuscule, vous devez rmettre la commande javaWelcome avec un W majuscule. Comme toujours, la casse doit tre respecte dans Java.

Chapitre 2

Lenvironnement de programmation de Java

31

Si linterprteur signale un problme concernant Welcome/java, vous avez accidentellement tap javaWelcome.java. Rmettez la commande javaWelcome.
m

Si vous avez tap javaWelcome et que la machine virtuelle ne trouve pas la classe Welcome, vriez si quelquun a congur la variable denvironnement CLASSPATH sur votre systme. Il nest gnralement pas conseill de dnir cette variable globalement, mme si certains installateurs de logiciels mal crits sous Windows le font. Pour lannuler temporairement dans la fentre shell actuelle, 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=
m

Si vous recevez un message derreur sur une nouvelle construction de langage, vriez que votre compilateur supporte Java SE 5.0. Si vous avez trop derreurs dans votre programme, tous les messages vont dler trs vite. Le compilateur envoie les messages vers la sortie derreur standard, ce qui rend leur capture difcile sils occupent plus dun cran. Utilisez loprateur shell 2> pour rediriger les erreurs vers un chier :
javac MyProg.java 2> 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. Il existe dautres IDE populaires mais Eclipse est actuellement le plus usit. Voici les tapes de dmarrage : 1. Aprs le dmarrage dEclipse, choisissez File -> New Project. 2. Slectionnez "Java Project" dans la bote de dialogue de lassistant (voir Figure 2.4). Ces captures dcran proviennent dEclipse 3.2. Votre version sera peut-tre lgrement diffrente. 3. 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.5. 4. Vriez que loption intitule "Create project in workspace" est dcoche. 5. Cliquez sur Finish. Le projet est maintenant cr.

32

Au cur de Java 2 - Notions fondamentales

Figure 2.4
Bote de dialogue New Project dans Eclipse.

Figure 2.5
Conguration dun projet Eclipse.

Chapitre 2

Lenvironnement de programmation de Java

33

6. 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 avec le code du programme (voir Figure 2.6).
Figure 2.6
Modication dun chier source avec Eclipse.

7. Cliquez du bouton droit sur le nom du projet (Welcome), dans le volet le plus gauche. Slectionnez Run -> Run As -> Java Application. Une fentre apparat au bas de la fentre. Le rsultat du programme sy afche (voir Figure 2.7).
Figure 2.7
Excution dun programme dans Eclipse.

34

Au cur de Java 2 - Notions fondamentales

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 (peuttre mme une erreur de syntaxe). Essayez dexcuter le chier en modiant 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.8) qui signalent un type string inconnu. Cliquez simplement sur le message. Le curseur se positionne sur la ligne correspondante dans la fentre ddition. Ce comportement vous permet de corriger rapidement vos erreurs.
ASTUCE
Bien souvent, un rapport derreur dEclipse est accompagn de licne dune ampoule. Cliquez dessus pour obtenir une liste des correctifs proposs.

Figure 2.8
Des messages derreur dans Eclipse.

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

Chapitre 2

Lenvironnement de programmation de Java

35

Excution dune application graphique


Le programme Welcome ne prsentait pas beaucoup dintrt. Excutons maintenant une application graphique. Il sagit dun afcheur simple de chiers image. Compilons ce programme et excutonsle depuis la ligne de commande. 1. Ouvrez une fentre shell. 2. Placez-vous sur le rpertoire CoreJavaBook/v1ch02/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 une image ouvrir (nous avons inclus quelques exemples de chiers 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 Listing 2.2). Vous apprendrez crer des programmes graphiques tels que celui-ci aux Chapitres 7 9.

36

Au cur de Java 2 - Notions fondamentales

Listing 2.2 : ImageViewer.java


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

/** * Un programme permettant dafficher des images. * @version 1.22 2007-05-21 * @author Cay Horstmann */ public class ImageViewer { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { 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) {

Chapitre 2

Lenvironnement de programmation de Java

37

// 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) { 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 enn, nous lafcherons dans un navigateur Web. Ouvrez un shell et placez-vous dans le rpertoire CoreJavaBook/v1ch02/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 lafcheur dapplets. La premire commande, qui nous est maintenant familire, est la commande dappel du compilateur Java. Celui-ci compile le chier source WelcomeApplet.java et produit le chier 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 nom de chier HTML plutt que le nom dun chier de classe java. Le contenu du chier WelcomeApplet.html est prsent dans le Listing 2.3 ci-aprs.

38

Au cur de Java 2 - Notions fondamentales

Figure 2.10
Lapplet WelcomeApplet dans lafcheur dapplets.

Listing 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 lafcheur de charger lapplet dont le code est stock dans WelcomeApplet.class. Cet afcheur dapplets ignore toutes les balises HTML except la balise applet. Malheureusement, la situation des navigateurs est un peu confuse.
m

Firefox prend en charge Java sous Windows, Linux et Mac OS X. Pour tester ces applets, tlchargez simplement la dernire version, rendez-vous sur le site http://java.com et utilisez le systme de vrication des versions pour chercher si vous devez installer le plug-in Java. 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, accdez ladresse http://java.com et installez le plug-in Java. Safari est intgr dans limplmentation Macintosh Java de Macintosh sous OS X, qui prend en charge Java SE 5.0 au moment o nous rdigeons.

A condition davoir un navigateur compatible avec une version moderne de Java, vous pouvez tenter de charger lapplet dans le navigateur.

Chapitre 2

Lenvironnement de programmation de Java

39

1. Dmarrez votre navigateur. 2. Slectionnez Fichier -> Ouvrir (ou lquivalent). 3. Placez-vous sur le rpertoire CoreJavaBook/v1ch02/WelcomeApplet. Vous devez voir apparatre le chier WelcomeApplet.html dans la bote de dialogue. Chargez le chier. 4. 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 dafcher la page Web de Cay. Cliquez sur le bouton Gary Cornell, lapplet lance lafchage 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 lafcheur dapplets. Cet afcheur na pas la possibilit denvoyer du courrier ou dafcher une page Web, il ignore donc vos demandes. Lafcheur 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. Pour terminer, le code de lapplet est prsent dans le Listing 2.4. A ce niveau de votre tude, contentez-vous de lexaminer rapidement. Nous reviendrons sur lcriture des applets au Chapitre 10.
Listing 2.4 : WelcomeApplet.java
import import import import java.awt.*; java.awt.event.*; java.net.*; javax.swing.*;

40

Au cur de Java 2 - Notions fondamentales

/** * Cet applet affiche un message des auteurs. * @version 1.22 2007-04-08 * @author Cay Horstmann */ public class WelcomeApplet extends JApplet { public void init() { EventQueue.invokeLater(new Runnable() { public void run() { 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(makeAction ("http://www.horstmann.com")); panel.add(cayButton); JButton garyButton = new JButton("Gary Cornell"); garyButton.addActionListener(makeAction ("mailto:gary_cornell@apress.com")); panel.add(garyButton); add(panel, BorderLayout.SOUTH); } }); } private ActionListener makeAction(final String urlString) { return new ActionListener() { public void actionPerformed(ActionEvent event) { try { getAppletContext().showDocument(new URL(urlString)); } catch(MalformedURLException e) { e.printStackTrace(); } } }; } }

Au cours de 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.

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.

42

Au cur de Java 2 - Notions fondamentales

Si vous tes un programmeur C++ expriment, vous pouvez vous contenter de parcourir ce chapitre et de vous concentrer 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 dafcher 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 modicateur daccs (ou encore un spcicateur daccs ou spcicateur de visibilit) ; les modicateurs dterminent les autres parties du programme qui peuvent tre utilises par notre exemple. Nous reparlerons des modicateurs 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 dnit 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). 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 chier du code source le mme nom que celui de la classe publique, avec lextension .java. Le code sera donc sauvegard dans un chier baptis FirstSample.java (rptons que la casse des caractres est importante, il ne faut pas nommer le chier firstsample.java). Si vous navez pas commis derreur de frappe en nommant le chier et en tapant le code source, la compilation de ce code produira un chier contenant le pseudo-code de la classe. Le compilateur Java nomme automatiquement le chier de pseudo-code FirstSample.class et le sauvegarde dans le mme rpertoire que le chier source. Lorsque tout cela est termin, lancez le programme laide de la commande suivante : javaFirstSample.

Chapitre 3

Structures fondamentales de la programmation Java

43

Ne spciez pas lextension .class. Lexcution du programme afche 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 spcie. Par consquent, vous devez crire une mthode main dans le chier 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 spcication du langage Java, la mthode main doit tre dclare public. La spcication est le document ofciel 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 didentication 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 spcication 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 ni par parler. Le lanceur Java de Java SE 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, an 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 cls staticvoid, songez simplement quils sont ncessaires la compilation du programme. Cette curieuse incantation vous sera familire la n du Chapitre 4. Rappelez-vous surtout que chaque application Java doit disposer dune mthode main dclare de la manire suivante :
public class NomDeClasse { public static void main(String[] args) { Instructions du programme } }

44

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 dnies lintrieur dune classe et qui noprent pas sur des objets. En Java, la mthode main est toujours statique. Prcisons enn 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 n 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 n 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 afche la chane sur la console. Elle passe ensuite la ligne an que chaque appel println afche la chane spcie 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".

Chapitre 3

Structures fondamentales de la programmation Java

45

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 goner la taille du code compil. Java propose trois types de commentaires. Le plus courant est //, qui dbute un commentaire allant jusqu la n 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 n dun long commentaire. Nous voyons ce type de dlimiteur au Listing 3.1.
Listing 3.1 : FirstSample.java
/** * Voici le premier exemple de programme de Au coeur de Java, Chapitre3 * @version 1.01 1997-03-22 * @author 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 suft 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 signie que le type de chaque variable doit tre dclar. Il existe huit types primitifs en Java. Quatre dentre eux sont des types entiers (integer), deux sont des types rels virgule ottante, 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.

46

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 int short long byte

Occupation en mmoire 4 octets 2 octets 8 octets 1 octet

Intervalle (limites incluses) 2 147 483 648 2 147 483 647 (un peu plus de 2 milliards) 32768 32767 9 223 372 036 854 775 808 9 223 372 036 854 775 807 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 chiers 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 efcace 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 de la mme manire sur toutes les machines, les plages de valeur des diffrents types sont xes. Le sufxe des entiers longs est L (par exemple, 4000000000L). Le prxe des entiers hexadcimaux est 0x (par exemple, 0xCAFE). Les valeurs octales ont le prxe 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.

Chapitre 3

Structures fondamentales de la programmation Java

47

Types virgule ottante


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

Type float double

Occupation en mmoire 4 octets 8 octets

Intervalle Environ 3.40282347E + 38F (6 ou 7 dcimales signicatives) Environ 1.79769313486231570E + 308 (15 chiffres signicatifs)

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 insufsante 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 (an dconomiser la mmoire). Les nombres de type float ont pour sufxe F, par exemple 3.402F. Les nombres dcimales exprims sans ce sufxe F, par exemple 3.402, sont toujours considrs comme tant du type double. Pour ces derniers, il est galement possible de spcier un sufxe D, par exemple 3.402D.
INFO
Depuis Java SE 5.0, vous pouvez spcier des nombres virgule ottante en hexadcimal. Par exemple, 0.125 = 23 quivaut 0x1.0p-3. Dans la notation hexadcimale, vous utilisez p, et non e, pour indiquer un exposant.

Tous les calculs en virgule ottante respectent la spcication IEEE 754. En particulier, il existe trois valeurs spciales en virgule ottante, qui servent indiquer les dpassements et les erreurs :
m m m

innit positive ; innit ngative ; NaN (Not a Number pas un nombre).

Par exemple, le rsultat de la division dun nombre positif par 0 est "innit 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 vrier 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"

48

Au cur de Java 2 - Notions fondamentales

ATTENTION
Les nombres virgule ottante ne conviennent pas aux calculs nanciers, 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 ottante 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
Le type char sert dcrire des caractres individuels. Le plus souvent, il sagira de 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 sexprimer sous forme de valeurs hexadcimales allant de \u0000 \uFFFF. Ainsi, \u2122 correspond au symbole de marque commerciale () et \u03C0 correspond la lettre grecque Pi (). Outre les squences dchappement \u, indiquant le codage en Unicode, il existe plusieurs squences dchappement pour les caractres spciaux, comme cela est indiqu au Tableau 3.3. Vous pouvez les utiliser dans des constantes et des chanes de caractres, comme \u2122 ou "Hello\ n". La squence dchappement \u (mais aucune des autres squences dchappement) peut mme tre utilise en dehors des constantes et des chanes de caractres entre apostrophes ou entre guillemets. Lexemple :
public static void main(String\u005B\u005D args)

est tout fait autoris, \u005B et \u005D sont les codages pour [ et ].
Tableau 3.3 : Squences dchappement pour les caractres spciaux

Squence dchappement \b \t \n \r \" \ \\

Nom Retour arrire Tabulation Sparation de ligne Retour chariot Guillemet Apostrophe Barre oblique inverse

Valeur Unicode \u0008 \u0009 \u000a \u000d \u0022 \u0027 \u005c

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.

Chapitre 3

Structures fondamentales de la programmation Java

49

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 dunication, dans les annes 1980, un code de largeur xe sur 2 octets tait plus que sufsant 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 l 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 insufsant pour dcrire tous les caractres Unicode. Nous ferons appel la terminologie pour expliquer comment ce problme a t rsolu en Java, et ce depuis Java SE 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 prxs 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 inutilise 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 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.

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.

50

Au cur de Java 2 - Notions fondamentales

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 spciant 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 dnie 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 signicatifs, ainsi que la casse de chaque caractre (minuscule ou majuscule). La longueur possible dun nom est thoriquement illimite.
ASTUCE
Si vous vous demandez lesquels des 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, dans lAnnexe, la liste des mots rservs). Il est possible de dclarer plusieurs variables sur une seule ligne, comme suit :
int i, j; // ce sont deux entiers en Java

Ce style de dclaration nest pas recommand. Si vous dnissez chaque variable sparment, vos programmes seront plus faciles lire.

Chapitre 3

Structures fondamentales de la programmation Java

51

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 difcile parfois de dnir 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 prxe "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 fonctionnalit de Java permet de dclarer et dinitialiser simultanment une variable en une seule instruction, de la faon suivante :
int vacationDays = 12;

Prcisons enn 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 dnition de variables. Par exemple,
int i = 10;

est une dnition, alors que


extern int i;

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

52

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 signie 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 dnit 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 dnition 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 utilis actuellement. Cest le mot cl final qui permet de dnir 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 ottante dans les

Chapitre 3

Structures fondamentales de la programmation Java

53

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 ottante par 0 donne un rsultat inni ou NaN. Les oprateurs arithmtiques peuvent tre utiliss en combinaison avec loprateur daffectation pour simplier 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 ottante, il est tonnamment difcile dobtenir cette portabilit. Le type double utilise 64 bits pour le stockage dune valeur numrique, mais certains processeurs emploient des registres virgule ottante 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 nalement 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 spcication 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 conictuels 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 ottante 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 ottante stricts. Si vous balisez une classe laide de strictfp, toutes ses mthodes utiliseront les calculs virgule ottante 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 ottante ne se pose pas pour la majorit des programmes courants. Nous nutiliserons pas le mot cl strictfp dans cet ouvrage.

54

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 : n++ ajoute 1 la valeur courante n et n-- retranche 1 cette valeur. Ainsi, cet exemple :
int n = 12; n++;

donne n la valeur 13. Comme ces oprateurs modient 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 "sufxe", o loprateur est situ aprs loprande : n++. Il peut galement tre plac en prxe : ++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 prxe, loprateur effectue dabord laddition ; en sufxe, 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 difciles 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 1 && expression 2

Chapitre 3

Structures fondamentales de la programmation Java

55

la valeur de la deuxime expression nest pas calcule si la premire a pu tre value false (puisque le rsultat nal 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. Enn, Java gre loprateur ternaire ? qui se rvle utile loccasion. Lexpression
condition? expression 1: expression 2

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

56

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 efcace. Cela signie que loprateur >> de C/C++ nest rellement dni 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, dni dans la classe System. Mais la mthode sqrt dans la classe Math nopre pas sur un objet. Une telle mthode est qualie 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);

dnit 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 enn deux constantes qui donnent les approximations les plus proches possibles des constantes mathmatiques et e :
Math.PI Math.E

Chapitre 3

Structures fondamentales de la programmation Java

57

ASTUCE
Depuis Java SE 5.0, vous pouvez viter le prxe Math pour les mthodes mathmatiques et les constantes en ajoutant la ligne suivante en haut de votre chier 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 ottante 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 dnition 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
Conversions lgales entre types numriques.

char

byte

short

int

long

float

double

Les six ches noires la Figure 3.1 indiquent les conversions sans perte dinformation. Les trois ches 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 ottante), 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.

58

Au cur de Java 2 - Notions fondamentales

m m m

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 dinformations. Les conversions risquant des pertes dinformations 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 ottant en un type entier fait perdre la partie fractionnaire. Si vous voulez arrondir un nombre virgule ottante 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 dinformations.
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

signie
(a && b) || c

Chapitre 3

Structures fondamentales de la programmation Java

59

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


a += b += c

signie
a += (b += c)

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


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.

Tableau 3.4 : Prsance des oprateurs

Oprateurs [] . () (appel de mthode) ! ~ ++ --+ (unaire) - (unaire) () (transtypage) new * /% + << >> >>> < <= > >= instanceof ==!= & ^ | && || ?: = += -= *= /= %= &= |= ^= <<= >>= >>>=

Associativit De la gauche vers la droite De la droite vers la gauche 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 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

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 conguration est sujette erreur. Une variable peut trop facilement contenir une valeur errone (comme 0 ou m). Depuis Java SE 5.0, vous pouvez dnir votre propre type numr ds quune situation se prsente. Un type numr possde un nombre ni de valeurs nommes. Par exemple,
enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };

60

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 dnie 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 prdnie appele, assez naturellement, String. Chaque chane entre guillemets est une instance de la classe String :
String e = ""; // une chane vide String greeting = "Hello";

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.

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

Chapitre 3

Structures fondamentales de la programmation Java

61

Voici un exemple :
int age = 13; String rating = "PG"+age;

qui donne la chane rating la valeur "PG13". Cette caractristique est utilise couramment pour lafchage. Par exemple, linstruction
System.out.println("The answer is "+answer);

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

Les chanes sont inaltrables


La classe String ne fournit pas de mthode permettant de modier 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 suft 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 modier individuellement des caractres dans une chane Java, la documentation indique que les objets de la classe String sont inaltrables. Tout comme le nombre 3 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 modier 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 lefcacit du partage des chanes surpassait linconvnient de la modication des chanes par extraction de sous-chanes et concatnation. Examinez vos propres programmes ; la plupart du temps, vous ne modiez sans doute pas les chanes vous vous contentez de les comparer. Bien entendu, il existe des situations o une manipulation directe se rvle plus efcace (par exemple lorsquon assemble des chanes partir de caractres individuels taps au clavier ou rcuprs dans un chier). Pour ces cas-l, Java fournit une classe spare, que nous dcrivons plus loin dans ce chapitre.

62

Au cur de Java 2 - Notions fondamentales

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+3, "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 nira par tre recycl. Si vous programmez en C++ et utilisez la classe string dnie 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 modier individuellement un caractre dans une chane.

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

Chapitre 3

Structures fondamentales de la programmation Java

63

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, 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 rednir == 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.

Points de code 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);

64

Au cur de Java 2 - Notions fondamentales

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.

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


is the set of integers

Le caractre

exige 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 . Pour viter ce problme, il vaut mieux ne pas utiliser le type char, qui est de trop bas niveau. 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--;

LAPI String
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 signication 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.

Chapitre 3

Structures fondamentales de la programmation Java

65

java.lang.String 1.0

char charAt(int index)

Renvoie lunit de code situe la position spcie. 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 spci.


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)

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

66

Au cur de Java 2 - Notions fondamentales

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

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

Chapitre 3

Structures fondamentales de la programmation Java

67

Lcran est divis en trois fentres. Une petite fentre en haut gauche afche 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 dler la deuxime fentre jusqu ce que le lien String soit visible, puis cliquez dessus.

Figure 3.3
Description de la classe String.

Faites dler 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 afcher sa description dtaille (voir Figure 3.5). Par exemple, si vous cliquez sur le lien compareToIgnoreCase, vous obtiendrez la description de la mthode compareToIgnoreCase.
ASTUCE
Affectez ds maintenant un signet la page docs/api/index.html dans votre navigateur.

68

Au cur de Java 2 - Notions fondamentales

Figure 3.4
Liste des mthodes de la classe String.

Figure 3.5
Description dtaille dune mthode de la classe String.

Chapitre 3

Structures fondamentales de la programmation Java

69

Construction de chanes
Pour le traitement dune saisie, vous devez parfois construire des chanes partir de caractres individuels ou de mots dun chier. La concatnation serait totalement inefcace dans ce cas. A chaque concatnation, un nouvel objet String est construit. Lopration prend du temps et consomme de la mmoire mais la classe StringBuilder permet dviter ce problme. Procdez comme suit pour construire une chane partir de petits lments. Construisez dabord un constructeur de chane vide :
StringBuilder builder = new StringBuilder();

Nous verrons les constructeurs et loprateur new en dtail au Chapitre 4. Chaque fois que vous devez ajouter une partie, appelez la mthode append.
builder.append(ch); // annexe un seul caractre builder.append(str); // annexe une chane

Lorsque vous avez termin de construire la chane, appelez la mthode toString. Vous obtiendrez un objet String avec la suite de caractres contenue dans le constructeur.
String completedString = builder.toString();

INFO
La classe StringBuilder a t introduite dans le JDK 5.0. Son prdcesseur, StringBuffer, est lgrement moins efcace mais permet plusieurs threads dajouter ou de supprimer des caractres. Si toute la modication des chanes survient dans un seul thread, ce qui est gnralement le cas, utilisez plutt StringBuilder. Les API des deux classes sont identiques.

Les notes dAPI suivantes contiennent les mthodes les plus importantes pour la classe StringBuilder.
java.lang.StringBuilder 5.0

StringBuilder()

Construit un constructeur de chane vide.


int length

Renvoie le nombre dunits de code du constructeur ou du tampon.


StringBuilder append(String str)

Annexe une chane et renvoie this.


StringBuilder append(char c)

Annexe une unit de code et renvoie this.


StringBuilder appendCodePoint(int cp)

Annexe un point de code, en le transformant en une ou deux units de code et renvoie this.
void setCharAt(int i, char c)

Dnit la ime unit de code sur c.


StringBuilder insert(int offset, String str)

Insre une chane la position offset et renvoie this.

70

Au cur de Java 2 - Notions fondamentales

StringBuilder insert(int offset, char c)

Insre une unit de code la position offset et renvoie this.


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

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 dafcher une sortie sur lunit de "sortie standard" (cest--dire la fentre de la console) en appelant System.out.println. La lecture dune "entre standard" System.in nest pas aussi simple. La lecture dune entre au clavier se fait en construisant un Scanner attach 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();

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 ottante. Le programme du Listing 3.2 demande le nom de lutilisateur et son ge, puis afche un message du style
Hello, Cay. Next year, youll be 46

Enn, ajoutez la ligne


import java.util.*;

Chapitre 3

Structures fondamentales de la programmation Java

71

au dbut du programme. La classe Scanner est dnie dans le package java.util. Ds que vous utilisez une classe qui nest pas dnie 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.
Listing 3.2 : InputTest.java
import java.util.*; /** * Ce programme montre une entre de console * @version 1.10 2004-02-10 * @author Cay Horstmann */ 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
La classe Scanner ne convient pas pour la lecture dun mot de passe partir dune console puisque lentre est tout fait visible pour tous. Java SE 6 introduit une classe Console rserve cet usage. Pour lire un mot de passe, utilisez le code suivant :
Console cons = System.console(); String username = cons.readLine("User name: "); char[] passwd = cons.readPassword("Password: ");

Pour des raisons de scurit, le mot de passe est renvoy dans un tableau de caractres plutt que dans une chane. Une fois que vous avez trait le mot de passe, crasez immdiatement les lments du tableau par une valeur de remplissage (le traitement des tableaux est trait plus loin dans ce chapitre). Le traitement des entres avec un objet Console nest pas aussi pratique quavec un Scanner. Vous ne pouvez lire quune ligne dentre la fois. Il nexiste pas de mthodes pour lire des mots ou des nombres individuels.

java.util.Scanner 5.0 Scanner(InputStream in)

Construit un objet Scanner partir du ux de saisie donn.

String nextLine()

Lit la prochaine ligne saisie.

72

Au cur de Java 2 - Notions fondamentales

String next()

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


int nextInt() double nextDouble()

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

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


java.lang.System 1.0 static Console console() 6

Renvoie un objet Console pour interagir avec lutilisateur via une fentre console si cette interaction est possible, null dans les autres cas. Un objet Console est disponible pour tout programme lanc dans une fentre console. Dans le cas contraire, la disponibilit dpend du systme.
java.io.Console 6 static char[] readPassword(String prompt, Object... args) static String readLine(String prompt, Object... args)

Afchent linvite et lisent la saisie utilisateur jusqu la n de la ligne dentre. Les paramtres args peuvent servir fournir des arguments de mise en forme, comme cest indiqu la section suivante.

Mise en forme de lafchage


Linstruction System.out.print(x) permet dafcher un nombre x la console. Cette instruction afchera 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);

afche
3333.3333333333335

Cela pose un problme si vous dsirez afcher, par exemple, des euros et des centimes. Dans les prcdentes versions de Java, lafchage des nombres posait quelques problmes. Heureusement, Java SE 5.0 a rapatri la vnrable mthode printf de la bibliothque C. Par exemple, lappel
System.out.printf("%8.2f", x);

afche x avec une largeur de champ de 8 caractres et une prcision de 2 caractres. En fait, lafchage contient un 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);

Chapitre 3

Structures fondamentales de la programmation Java

73

Chacun des spcicateurs de format qui commencent par le caractre % est remplac par largument correspondant. Le caractre de conversion qui termine un spcicateur de format indique le type de la valeur mettre en forme : f est un nombre virgule ottante, 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 d x o f e g a s c b h tx % n

Type Entier dcimal Entier hexadcimal Entier octal Virgule xe, virgule ottante Virgule ottante exponentielle Virgule ottante gnrale (le plus court entre e et f) Virgule ottante hexadcimale Chane Caractre Valeur boolenne Code de hachage Date et heure Symbole du pourcentage Sparateur de ligne fonction de la plate-forme

Exemple 159 9f 237 15.9 1.59e+01

0x1.fccdp3 Hello H true 42628b2 Voir Tableau 3.7 %

Vous pouvez galement spcier 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);

afche
3,333.33

Vous pouvez afcher 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.

74

Au cur de Java 2 - Notions fondamentales

Tableau 3.6 : Drapeaux pour printf

Drapeau + espace 0 ( , # (pour format f) # (pour format x ou o) $

Rle Afche le signe des nombres positifs et ngatifs. Ajoute un espace avant les nombres positifs. Ajoute des zros pralables. Justie le champ gauche. Entoure le nombre ngatif de parenthses. Ajoute des sparateurs de groupe. Inclut toujours une dcimale. Ajoute le prxe 0x ou 0. Indique lindice de largument mettre en forme ; par exemple, %1$d %1$x afche le premier argument en dcimal et hexadcimal. Met en forme la mme valeur que la spcication prcdente ; par exemple, %d %<x afche le mme nombre en dcimal et hexadcimal.

Exemple +3333.33 | 3333.33| 003333.33 |3333.33| (3333.33) 3,333.33 3,333 0xcafe 159 9F

<

159 9F

Vous pouvez utiliser la mthode statique String.format pour crer une chane mise en forme sans lafcher :
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());

afche 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 C F D T r R

Type Date et heure compltes Date au format ISO 8601 Date au format amricain (mois/jour/anne) Heure sur 24 heures Heure sur 12 heures Heure sur 24 heures sans secondes

Exemple Mon Feb 09 18:05:19 PST 2004 2004-02-09 02/09/2004 18:05:19 06:05:19 pm 18:05

Chapitre 3

Structures fondamentales de la programmation Java

75

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

Caractre de conversion Y y C B b ou h m d e A a j H k I l M S L N P p z Z s Q

Type Anne sur quatre chiffres (avec zros pralables, le cas chant) Deux derniers chiffres de lanne (avec zro pralable, le cas chant) Deux premiers chiffres de lanne (avec zro pralable, le cas chant) Nom complet du mois Nom du mois abrg Mois sur deux chiffres (avec zro pralable, le cas chant) Jour sur deux chiffres (avec zro pralable, le cas chant) Jour sur deux chiffres (avec zro pralable, le cas chant) Jour de la semaine complet Jour de la semaine abrg Jour de lanne sur trois chiffres (avec zros pralables, le cas chant), entre 001 et 366 Heure sur deux chiffres (avec zro pralable, le cas chant), entre 00 et 23 Heure sur deux chiffres (sans zro pralable), entre 0 et 23 Heure sur deux chiffres (avec zro pralable, le cas chant), entre 01 et 12 Heure sur deux chiffres (sans zro pralable), entre 1 et 12 Minutes sur deux chiffres (avec zro pralable, le cas chant) Secondes sur deux chiffres (avec zro pralable, le cas chant) Millimes de seconde, sur trois chiffres (avec zros pralables, le cas chant) Nanosecondes sur neuf chiffres (avec zros pralables, le cas chant) Indicateur du matin ou de laprs-midi en majuscules Indicateur du matin ou de laprs-midi en minuscules Dcalage numrique RFC 822 du GMT Fuseau horaire Secondes depuis le 1970-01-01 00:00:00 GMT Millimes de seconde depuis le 1970-01-01 00:00:00 GMT

Exemple 2004 04 20 February Feb 02 09 9 Monday Mon 069 18 18 06 6 05 19 047 047000000 PM pm -0800 PST 1078884319 1078884319047

76

Au cur de Java 2 - Notions fondamentales

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

afche
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.6 prsente un diagramme syntaxique des spcicateurs de mise en forme.
format-specifier:

%
argument index

conversion character

flag

width

precision

t Figure 3.6
Syntaxe du spcicateur de mise en forme.

conversion character

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

Entre et sortie de chiers


Pour lire partir dun chier, construisez un objet Scanner partir dun objet File, comme ceci :
Scanner in = new Scanner(new File("myfile.txt"));

Si le nom du chier contient des barres obliques inverses, noubliez pas de les chapper toutes avec une autre barre oblique inverse : "c:\\mydirectory\\myfile.txt".

Chapitre 3

Structures fondamentales de la programmation Java

77

Vous pouvez maintenant lire partir du chier, en utilisant lune des mthodes Scanner dj dcrites. Pour crire dans un chier, construisez un objet PrintWriter. Dans le constructeur, fournissez simplement le nom du chier :
PrintWriter out = new PrintWriter("myfile.txt");

Si le chier nexiste pas, vous pouvez simplement utiliser les commandes print, println et printf comme pour lafchage sur System.out.
ATTENTION
Vous pouvez construire un Scanner avec un paramtre de chane, mais le scanner interprte la chane sous forme de donnes et non de nom de chier. Par exemple, si vous appelez
Scanner in = new Scanner("myfile.txt"); // ERREUR ?

le scanner voit dix caractres de donnes : m, y, f, etc. Ce nest probablement pas ce qui a t prvu dans ce cas.

INFO
Lorsque vous prcisez un nom de chier relatif, comme "myfile.txt", "mydirectory/myfile.txt" ou "../ myfile.txt", le chier est situ par rapport au rpertoire dans lequel la machine virtuelle Java pralable au JDK 5.0 a t dmarre. Si vous avez lanc le programme partir dun shell de commande, en excutant
java MyProg

le rpertoire de dpart correspond au rpertoire courant du shell de commande. Toutefois, si vous utilisez un environnement de dveloppement intgr, le rpertoire de dpart est contrl par lIDE. Vous trouverez lemplacement du rpertoire avec cet appel :
String dir = System.getProperty("user.dir");

Si vous rencontrez des difcults localiser les chiers, envisagez dutiliser des noms de chemin absolus, comme "c:\\mydirectory\\myfile.txt" ou "/home/me/mydirectory/myfile.txt".

Vous lavez vu, vous pouvez accder aux chiers aussi simplement que vous utilisez System.in et System.out. Il y a toutefois un pige : si vous construisez un Scanner avec un chier qui nexiste pas ou un PrintWriter avec un nom de chier qui ne peut pas tre cr, une exception survient. Le compilateur Java considre ces exceptions plus srieuses quune exception "diviser par zro", par exemple. Nous verrons au Chapitre 11 diverses manires de grer les exceptions. Pour lheure, indiquez simplement au compilateur que vous tes conscient de la possibilit dune exception de chier introuvable ("le not found"). Pour cela, balisez la mthode main par une clause throws, comme ceci :
public static void main(String[] args) throws FileNotFoundException { Scanner in = new Scanner(new File("myfile.txt")); . . . }

Vous avez maintenant vu comment lire et crire des chiers contenant des donnes de texte. Pour aborder des sujets plus avancs, comme le traitement des diffrents codages de caractres, le traitement des donnes binaires, la lecture des rpertoires et lcriture de chiers zip, consultez le Chapitre 1 du Volume II.

78

Au cur de Java 2 - Notions fondamentales

INFO
Lorsque vous lancez un programme partir dun shell de commande, vous pouvez utiliser la syntaxe de redirection de votre shell et attacher un chier System.in et System.out:
java MyProg < myfile.txt > output.txt

Vous navez plus vous inquiter ensuite de grer lexception FileNotFoundException.

java.util.Scanner 5.0

Scanner(File f)

Construit un Scanner qui lit les donnes partir du chier donn.


Scanner(String data)

Construit un Scanner qui lit les donnes partir de la chane donne.


java.io.PrintWriter 1.1

PrintWriter(File f)

Construit un PrintWriter qui crit des donnes dans le chier concern.


PrintWriter(String fileName)

Construit un PrintWriter qui crit des donnes dans le chier ayant le nom concern.
java.io.File 1.0

File(String fileName)

Construit un objet File qui dcrit un chier avec le nom donn. Sachez que le chier na pas besoin dexister pour linstant.

Flux dexcution
Comme tout langage de programmation, Java gre les instructions conditionnelles et les boucles an de contrler le ux dexcution (ou ux 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 ux dexcution de Java sont comparables celles de C et de C++, quelques 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). Enn, Java SE 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 :

Chapitre 3

Structures fondamentales de la programmation Java

79

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. Lexemple suivant 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 rednir une variable lintrieur dun bloc imbriqu. La dnition la plus interne cache alors la dnition externe. Cela constitue une source derreur, et Java ne lautorise pas.

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

80

Au cur de Java 2 - Notions fondamentales

Figure 3.7
Organigramme de linstruction if.
NON yourSales target

OUI

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

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


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

Chapitre 3

Structures fondamentales de la programmation Java

81

Figure 3.8
Organigramme de linstruction if/else.

OUI

yourSales

target

NON

performance =Satisfactory

performance =Unsatisfactory

bonus= 100+0.01* (yourSalestarget)

bonus=0

la directive else appartient au second if. Bien entendu, il est conseill dutiliser les accolades pour prciser ce code :
if (x <=0) { if (x == 0) sign = 0; else sign = -1; }

Des squences rptes if . . . else if . . . sont trs frquentes (voir Figure 3.9). Par exemple :
if (yourSales >= 2 * target) { performance = "Excellent"; bonus = 1000; } else if (yourSales >= 1.5 * target) { performance = "Fine"; bonus = 500; } else if (yourSales >= target) { performance = "Satisfactory"; bonus = 100; } else { System.out.println("Youre fired"); }

82

Au cur de Java 2 - Notions fondamentales

yourSales 2*target

OUI

performance =Excellent

bonus=1000

NON

yourSales 1.5*target

OUI

performance =Fine

bonus=500

NON

OUI yourSales target

performance =Satisfactory

bonus=100

NON

Print Youre fired

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

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.10).

Chapitre 3

Structures fondamentales de la programmation Java

83

Figure 3.10
Organigramme de linstruction while.
NON balance < goal

OUI

update balance

years++

Print years

Dans le Listing 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 spci. 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; double interest = balance * interestRate / 100; balance += interest; years++; } System.out.println(years + " years.");

84

Au cur de Java 2 - Notions fondamentales

Ne vous ez pas ce programme pour prvoir votre retraite. Nous avons laiss de ct quelques dtails comme lination 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 n de boucle. Pour cela, employez une boucle do/while, dont voici la syntaxe :
do instruction while (condition);

Cette boucle excute linstruction (gnralement un bloc) avant de tester la condition. Le programme rexcute le bloc avant deffectuer un nouveau test, et ainsi de suite. Par exemple, le code du Listing 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.11). Ce programme est un bon exemple dune boucle devant tre excute au moins une fois, car lutilisateur doit pouvoir vrier le solde avant de dcider sil est sufsant pour assurer sa retraite.
Listing 3.3 : Retirement.java
import java.util.*; /** * Ce programme prsente une boucle <code>while</code>. * @version 1.20 2004-02-10 * @author Cay Horstmann */ public class Retirement { public static void main(String[] args) { // lire les infos entres Scanner in = 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();

Chapitre 3

Structures fondamentales de la programmation Java

85

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

Figure 3.11
Organigramme de linstruction do/while.
actualiser le solde

imprimer le solde demander "Ready to retire? (Y/N)"

lire l'entre

OUI input="N"

NON

86

Au cur de Java 2 - Notions fondamentales

Listing 3.4 : Retirement2.java


import java.util.*; /** * Ce programme prsente une boucle <code>do/while</code>. * @version 1.20 2004-02-10 * @author Cay Horstmann */ 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(); } while (input.equals("N")); } }

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.12, le code suivant afche les nombres 1 10 sur lcran :
for (int i = 1; i <= 10; i++) System.out.println(i);

Chapitre 3

Structures fondamentales de la programmation Java

87

Le premier lment de linstruction for contient gnralement linitialisation du compteur. Le deuxime lment fournit la condition de test qui sera vrie 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 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.12
Organigramme de linstruction for.
i=1

10

NON

OUI

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!");

88

Au cur de Java 2 - Notions fondamentales

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

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

En particulier, si vous dnissez 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 nale 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 dnir 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--; }

Le Listing 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)

Chapitre 3

Structures fondamentales de la programmation Java

89

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

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

Listing 3.5 : LotteryOdds.java


import java.util.*; /** * Ce programme prsente une boucle <code>for</code>. * @version 1.20 2004-02-10 * @author Cay Horstmann */ 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!"); } }

90

Au cur de Java 2 - Notions fondamentales

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.13, 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: // entre incorrecte . . . break; }

Lexcution commence ltiquette case dont la valeur correspond la valeur de slection, puis elle se poursuit jusqu une instruction break ou jusqu la n du bloc switch. Si aucune correspondance nest trouve, la clause default est excute, si elle existe.
ATTENTION
Il est possible dexcuter plusieurs alternatives. Si vous oubliez dajouter la clause break la n dune alternative, lexcution se poursuit avec lalternative qui suit ! Ce comportement est trs dangereux et constitue une cause commune derreur. Cest pourquoi nous nutilisons pas linstruction switch dans nos programmes.

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

Chapitre 3

Structures fondamentales de la programmation Java

91

Figure 3.13
Organigramme de linstruction switch.
OUI choix = 1

...

NON

OUI choix = 2

...

NON

OUI choix = 3

...

NON

OUI choix = 4

...

NON (par dfaut) mauvaise saisie

92

Au cur de Java 2 - Notions fondamentales

Lorsque vous utilisez linstruction switch avec des constantes numres, vous navez pas besoin de fournir le nom de lnumration dans chaque tiquette, il est dduit de la valeur switch. Par exemple,
Size sz = . . .; switch (sz) { case SMALL: // inutile dutiliser Size.SMALL . . . break; . . . }

Interrompre le ux 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++; }

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.

Chapitre 3

Structures fondamentales de la programmation Java

93

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 le break tiquet 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

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 enn une instruction continue qui, comme linstruction break, interrompt le ux normal dexcution. Linstruction continue transfre le contrle en tte de la boucle englobante la plus interne. En voici un exemple :

94

Au cur de Java 2 - Notions fondamentales

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 ottant nest pas sufsante, 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 ottante. Utilisez la mthode statique valueOf pour transformer un nombre ordinaire en grand nombre :
BigInteger a = BigInteger.valueOf(100);

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)

Chapitre 3

Structures fondamentales de la programmation Java

95

INFO C++
Contrairement C++, Java ne prvoit pas de surcharge programmable des oprateurs. Il nest pas possible au programmeur de la classe BigInteger de rednir 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 dans leurs propres classes.

Le Listing 3.6 montre le programme lotteryOdds du Listing 3.5 modi, an 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 du Listing 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));

Listing 3.6 : BigIntegerTest.java


import java.math.*; import java.util.*; /** * Ce programme utilise les grands nombres pour calculer les * chances de gagner le gros lot la loterie * @version 1.20 2004-02-10 * @author Cay Horstmann */ 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);

96

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.

Chapitre 3

Structures fondamentales de la programmation Java

97

Vous dclarez une variable tableau en spciant 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 de 100 entiers. Lorsque vous crez un tableau de nombres, tous les lments sont initialiss avec 0 (les tableaux de boolean sont initialiss avec false, les tableaux dobjets le sont avec des valeurs null).
INFO
Vous pouvez dnir 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 modier sa taille (mais vous pouvez changer un lment individuel du tableau). Si vous devez modier 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"


Java SE 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.

98

Au cur de Java 2 - Notions fondamentales

La boucle for amliore


for (variable: collection) instruction

dnit 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 II. Par exemple,
for (int element: a) System.out.println(element);

afche 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 nal, 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 n, 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.
ASTUCE
Il existe une mthode encore plus simple pour afcher toutes les valeurs dun tableau, et ce grce la mthode toString de la classe Arrays. Lappel Arrays.toString(a) renvoie une chane contenant les lments du tableau, insrs entre crochets et spars par des virgules, comme "[2, 3, 5, 7, 11, 13]". Pour afcher le tableau, appelez simplement
System.out.println(Arrays.toString(a));

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.

Chapitre 3

Structures fondamentales de la programmation Java

99

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

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.14 montre le rsultat. Si vous voulez effectivement copier toutes les valeurs dun tableau dans un autre, il faut employer la mthode copyTo de la classe Arrays.
int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);

Le second paramtre correspond la longueur du nouveau tableau. Cette mthode est souvent employe pour augmenter la taille dun tableau :
luckyNumbers = Arrays.copyOf(luckyNumbers, 2 * luckyNumbers.length);

Les autres lments sont remplis de 0 si le tableau contient des nombres, false si le tableau contient des valeurs boolennes. A linverse, si la longueur est infrieure celle du tableau initial, seules les valeurs initiales sont copies.
Figure 3.14
Copie dune variable tableau.
smallPrimes = luckyNumbers = 2 3 5 7 11 12

100

Au cur de Java 2 - Notions fondamentales

INFO
Avant Java SE 6, la mthode arraycopy de la classe System servait copier des lments dun tableau dans un autre. Sa syntaxe est la suivante :
System.arraycopy(from, fromIndex, to, toIndex, count);

Le tableau to doit disposer de sufsamment despace pour contenir les lments copis.

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.15 :
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: 4: 5: 6: 1001 1002 1003 5 7 11 13

Figure 3.15
Copie des valeurs vers un tableau.
smallPrimes = 2 3 5 7 11 13

luckyNumbers =

1001 1002 1003 5 7 11 13

INFO C++
Un tableau Java est assez diffrent dun tableau 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

Chapitre 3

Structures fondamentales de la programmation Java

101

nest pas la mme chose que


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

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

En Java, loprateur [] est prdni pour effectuer une vrication 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 spcis 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,"); // 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 afche 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".

102

Au cur de Java 2 - Notions fondamentales

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 efcace 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 n de cette section. Le programme du Listing 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 afcher :
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 :


int[] result = new int[k];

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

Nous dnissons le ime rsultat comme le numro de cet indice. Au dpart, il sagit simplement de r + 1, 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);

Chapitre 3

Structures fondamentales de la programmation Java

103

Listing 3.7 : LotteryDrawing.java


import java.util.*; /** * Ce programme prsente la manipulation des tableaux. * @version 1.20 2004-02-10 * @author Cay Horstmann */ 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]; // 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.util.Arrays 1.2 static String toString(type[] a) 5.0

Renvoie une chane avec les lments de a, entre parenthses et dlimits par des virgules. Paramtres :

Un tableau de type int, long, short, char, byte, boolean, float ou double.

static type copyOf(type[] a, int length) 6 static type copyOf(type[] a, int start, int end) 6

104

Au cur de Java 2 - Notions fondamentales

Renvoient un tableau du mme type que a, de longueur length ou end- start, rempli des valeurs de a. Paramtres : a start end un tableau de type int, long, short, char, byte, boolean, float ou double. lindice de dpart (inclus). lindice de n (exclu). Peut tre suprieur a.length, auquel cas le rsultat est complt par des 0 ou des valeurs false. la longueur de la copie. Si length est suprieur a.length, le rsultat est complt par des 0 ou des valeurs false. Sinon, seules les valeurs length initiales sont copies.

length

static void sort(type[] a)

Trie le tableau en utilisant un algorithme QuickSort adapt. Paramtres :


Tableau de type int, long, short, char, byte, float ou double.


6

static int binarySearch(type[] a, type v) static int binarySearch(type[] a, int start, int end type v)

Utilisent lalgorithme de recherche binaire 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 : a start end v

Tableau tri de type int, long, short, char, byte, float ou double. lindice de dpart (inclus) lindice de n (exclu) 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 : a v

Tableau de type int, long, short, char, byte, boolean, oat 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. Paramtres : a, b Tableau de type int, long, short, char, byte, boolean, float ou double.

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.

Chapitre 3

Structures fondamentales de la programmation Java

105

Paramtres :

from fromIndex to toIndex count

un tableau de nimporte quel type (le Chapitre 5 explique pourquoi il sagit dun paramtre de type Object). lindice de dpart partir duquel copier les lments. un tableau du mme type que from. lindice de dpart auquel copier les lments. le nombre dlments copier.

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 % 10 000,00 11 000,00 12 100,00 13 310,00 14 641,00 16 105,10 17 715,61 19 487,17 21 435,89 23 579,48

11 % 10 000,00 11 100,00 12 321,00 13 676,31 15 180,70 16 850,58 18 704,15 20 761,60 23 045,38 25 580,37

12 % 10 000,00 11 200,00 12 544,00 14 049,28 15 735,19 17 623,42 19 738,23 22 106,81 24 759,63 27 730,79

13 % 10 000,00 11 300,00 12 769,00 14 428,97 16 304,74 18 424,35 20 819,52 23 526,05 26 584,44 30 040,42

14 % 10 000,00 11 400,00 12 996,00 14 815,44 16 889,60 19 254,15 21 949,73 25 022,69 28 525,86 32 519,49

15 % 10 000,00 11 500,00 13 225,00 15 208,75 17 490,06 20 113,57 23 130,61 26 600,20 30 590,23 35 178,76

La manire vidente de stocker cette information est un tableau deux dimensions (ou matrice) que nous appellerons balances. 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];

106

Au cur de Java 2 - Notions fondamentales

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

Le Listing 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. Elle parcourt plutt les lignes, qui sont elles-mmes des tableaux une dimension. Pour visiter tous les lments dun tableau bidimensionnel a, imbriquez deux boucles, comme ceci :
for (double[] row: a) for (double b: row) faire quelque chose avec value

ASTUCE
Pour afcher une liste rapide et dsorganise des lments dun tableau bidimensionnel, appelez
System.out.println(Arrays.deepToString(a));

La sortie est mise en forme ainsi :


[[16, 3, 2, 13], [5, 10, 11, 8], [9, 6, 7, 12], [4, 15, 14, 1]]

Chapitre 3

Structures fondamentales de la programmation Java

107

Listing 3.8 : CompoundInterest.java


/** * Ce programme montre comment stocker des donnes tabulaires * dans un tableau 2D. * @version 1.40 2004-02-10 * @author Cay Horstmann */ public class CompoundInterest { public static void main(String[] args) { final double 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[][] balances = new double[NYEARS][NRATES]; // dfinir les soldes initiaux 10000 for (int j = 0; j < balance[0].length; j++) balance[0][j] = 10000; // calculer lintrt des annes venir for (int i = 1; i < balances.length; i++) { for (int j = 0; j < balances[i].length; j++) { // rcup. solde anne prcdente de la ligne prcdente double oldBalance = balances[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(); } } }

108

Au cur de Java 2 - Notions fondamentales

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". 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.16).
Figure 3.16
balances = Un tableau deux dimensions. balances[1] = 10000.0 10000.0 10000.0 10000.0 10000.0 10000.0

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. Cest en soi un tableau et balances[i] [j] fait rfrence llment j de ce tableau. Comme les ranges de tableau sont accessibles individuellement, vous pouvez aisment les intervertir !
double[] temp = balances[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 faut "choisir j nombres parmi i nombres" :

Chapitre 3

Structures fondamentales de la programmation Java

109

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 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 (int k = 0; k < odds[n].length; k++) { // calculer lotteryOdds . . . odds[n][k] = lotteryOdds; }

Le Listing 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.

110

Au cur de Java 2 - Notions fondamentales

Listing 3.9 : LotteryArray.java


/** * Ce programme montre un tableau triangulaire. * @version 1.20 2004-02-10 * @author Cay Horstmann */ 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]; // 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(); } } }

Vous connaissez maintenant les structures de programmation de base du langage Java. Le chapitre suivant traite de la programmation oriente objet en Java.

4
Objets et classes
Au sommaire de ce chapitre

Introduction la programmation oriente objet Utilisation des classes prdnies Construction de vos propres classes Champs et mthodes statiques Paramtres des mthodes Construction dun objet Packages Le chemin de classe Commentaires pour la documentation Conseils pour la conception de classes
Lobjectif de ce chapitre est :
m 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++).

112

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 vous devez tre familiaris avec la POO (voir Figure 4.1) pour devenir productif en Java. Un programme est constitu dobjets possdant certaines proprits prsentes aux utilisateurs, ainsi quune implmentation cache. De nombreux objets de vos programmes seront extraits tels quels dune bibliothque, dautres seront personnaliss. 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 spcications, vous ne vous proccupez pas de savoir comment ils ont t implments. En POO, vous ne vous proccupez pas de savoir comment un objet est implment, pourvu quil excute ce que vous souhaitez. La programmation structure traditionnelle consiste concevoir un ensemble de fonctions (ou algorithmes) permettant de rsoudre un problme. Une fois ces fonctions dtermines, 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 que vous imposeriez aux donnes an 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. Pour les problmes mineurs, la dcomposition en procdures fonctionne trs bien. Mais les objets fonctionnent mieux pour les gros problmes. Imaginez un navigateur Web simple. Son implmentation pourrait ncessiter jusqu 2 000 procdures, toutes manipulant un ensemble de donnes globales. Dans le style orient objet, il pourrait y avoir 100 classes, avec une moyenne de 20 mthodes par classe (voir la comparaison entre programmation procdurale et POO). Cette dernire structure est plus simple saisir pour un programmeur. La dcouverte des bogues est facilite. Supposons que les donnes dun objet particulier se trouvent dans un tat incorrect. Il sera plus facile de rechercher le coupable parmi les 20 mthodes ayant eu accs aux donnes que parmi 2 000 procdures.
Figure 4.1
Programmation procdurale ou oriente objet.
procdure procdure procdure procdure procdure mthode mthode Donnes dobjet mthode mthode mthode mthode Donnes dobjet

Donnes globales

Donnes dobjet

Chapitre 4

Objets et classes

113

Les classes
Une classe est le modle ou la matrice de lobjet effectif. Cela nous amne comparer 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 et dcrire les objets des domaines de problmes appartenant vos applications. 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 spcique, qui est une instance dune classe, a des valeurs spciques dans ses champs dinstance. Le jeu de ces valeurs est ltat actuel de lobjet. Chaque fois que vous appelez une mthode sur un objet, son tat peut changer. 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 signie quune classe peut compltement modier 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 didentier 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 ?

m m

114

Au cur de Java 2 - Notions fondamentales

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 modication dans ltat dun objet doit tre la consquence dappels de mthodes (si ltat de lobjet change sans quun appel de mthode soit intervenu, cela signie que la rgle de lencapsulation a t viole). Nanmoins, ltat dun objet ne dcrit pas compltement celui-ci, car chaque objet possde une identit spcique. 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 inuence 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.

Identication des classes


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. Une rgle simple dans lidentication 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 m m m 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 didentier 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 les noms et les verbes qui sont importants pour le dveloppement de vos propres classes.

Chapitre 4

Objets et classes

115

Relations entre les classes


Les relations les plus courantes entre les classes sont :
m m 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 vrier 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. 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 modications apportes ventuellement B! (Et cela signie quune modication 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. La relation dagrgation ou "possde" est facile comprendre, car elle est concrte ; par exemple, un objet Order contient des objets Item. Cette relation signie 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 spcique 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 (Unied Modeling Language) pour reprsenter les diagrammes de classe qui dcrivent la relation entre les classes. Un exemple est montr la Figure 4.2. Vous dessinez les classes sous la forme de rectangles et les relations sont reprsentes par des ches ayant diffrents aspects. Le Tableau 4.1 montre les styles de ches les plus couramment utiliss.

116

Au cur de Java 2 - Notions fondamentales

Figure 4.2
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 (http://www.ibm.com/software/awdtools/developer/modeler/rose) et Together (http://www.borland.com/us/products/ 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://violet.sourceforge.net).

Tableau 4.1 : Notation UML pour reprsenter la relation entre classes

Relation Hritage Hritage dinterface Dpendance Agrgation Association Association dirige

Connecteur UML

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. Un bon exemple de cette anomalie est constitu par la classe Math. Vous avez vu que lon peut utiliser

Chapitre 4

Objets et classes

117

les mthodes de la classe Math, telles que Math.random, sans avoir besoin de savoir comment elles sont implmentes il suft 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 spcier 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 spcier 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 spcies 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 gure ? 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. Sachez dailleurs que la bibliothque Java est un peu embrouille et quune grosse rorganisation est en cours ; voir ladresse http://jcp.org/en/jsr/detail?id=310.

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

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
Cration dun nouvel objet.
birthday = Date

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

dnit 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 dnir la variable pour quelle fasse rfrence un objet existant :


deadline = birthday;

Maintenant, les deux variables font rfrence au mme objet (voir Figure 4.4). 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();

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.

Chapitre 4

Objets et classes

119

Figure 4.4
Variables objet faisant rfrence au mme objet.

birthday = birthday =

Date

Il est possible de donner explicitement la valeur null une variable objet an 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 locales 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 prdnies. En Java, il faut utiliser la mthode clone pour obtenir une copie complte dun objet.

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 xe (appel epoch ou poque), qui est le 1er janvier 1970 00:00:00 UTC. UTC est le temps universel (Coordinated Universal Time), le standard scientique 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, Second Edition, de Nachum Dershowitz et Edward M. Reingold (Cambridge University Press, 2001), 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("Still 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 signie que le concepteur de la bibliothque a admis quelles nauraient jamais d y gurer.

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 spcique 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 simplier ces manipulations, il existe des constantes comme Calendar.DECEMBER :
new GregorianCalendar(1999, Calendar.DECEMBER, 31)

Vous pouvez aussi dnir 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 dnie. 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 modier 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 n 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 dnies 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.

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 dnir lanne, le mois et le jour en un seul appel :
deadline.set(2001, Calendar.APRIL, 15);

Vous pouvez enn 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 modient ltat de lobjet. Les mthodes qui modient des champs dinstance sont appeles mthodes daltration (mutator), celles qui se contentent daccder aux champs de linstance, sans les modier, sont appeles mthodes daccs (accessor).
INFO C++
En C++, le sufxe 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 prxer 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 dnissent 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 dnir 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, dnissez lheure, puis appelez la mthode get :
GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(hireDay); int year = calendar.get(Calendar.YEAR);

Chapitre 4

Objets et classes

123

Nous allons conclure cette section avec un programme qui tire parti de la classe GregorianCalendar. Le programme afche 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 astrisque (*). Vous le voyez, le programme sait comment calculer les dures dun mois et dun jour de la semaine. Examinons les tapes cls du programme. Tout dabord, nous construisons un objet calendrier qui est initialis avec la date courante :
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 dnie Calendar.SUNDAY si le premier jour du mois est un dimanche, Calendar.MONDAY sil sagit dun lundi, etc. Ces valeurs correspondent en fait aux entiers 1, 2, 7 mais mieux vaut ne pas crire de code reposant sur ces connaissances. Remarquez que la premire ligne du calendrier est indente de manire que le 1 er du mois corresponde au jour de la semaine qui convient. Cest un peu difcile puisquil existe diverses conventions dnissant le jour qui commence la semaine. Aux Etats-Unis, il sagit du dimanche, la semaine se terminant le samedi, alors quen Europe elle dmarre le lundi et se termine le dimanche. La machine virtuelle Java connat les paramtres rgionaux de lutilisateur courant. Ils dterminent les conventions de mise en forme locales, notamment le dbut de la semaine et le nom des jours.
ASTUCE
Pour voir la sortie du programme avec un autre paramtre rgional, ajoutez une ligne de la sorte au dbut de la mthode main:
Locale.setDefault(Locale.ITALY);

La mthode getFirstDayOfWeek rcupre le premier jour de la semaine dans les paramtres rgionaux actuels. Pour dterminer lindentation requise, nous soustrayons 1 du jour de lobjet de calendrier, jusqu atteindre le premier jour de la semaine.
int firstDayOfWeek = d.getFirstDayOfWeek(); int indent = 0;

124

Au cur de Java 2 - Notions fondamentales

while (weekday != firstDayOfWeek) { indent++; d.add(Calendar.DAY_OF_MONTH, -1); weekday = d.get(Calendar.DAY_OF_WEEK); }

Nous afchons ensuite len-tte avec les noms des jours. Ils sont disponibles dans la classe DateFormatSymbols.
String [] weekdayNames = new DateFormatSymbols().getShortWeekdays();

La mthode getShortWeekdays renvoie une chane avant les noms des jours en version courte dans la langue de lutilisateur (par ex "Sun", "Mon", etc., en anglais). Le tableau est index en fonction des valeurs des jours. Voici la boucle permettant dafcher len-tte :
do { System.out.printf("%4s", weekdayNames[weekday]); d.add(Calendar.DAY_OF_MONTH, 1); weekday = d.get(Calendar.DAY_OF_WEEK); } while (weekday != firstDayOfWeek); System.out.println();

Vous tes maintenant prt afcher le corps du calendrier. Nous indentons la premire ligne et dnissons lobjet date sur le dbut du mois. Nous entrons une boucle dans laquelle d parcourt les jours du mois. Dans chaque itration, nous afchons la valeur de la date. Si d correspond la date du jour, la date est indique avec un *. Si nous atteignons le dbut de chaque nouvelle semaine, nous afchons une nouvelle 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. Le Listing 4.1 montre le programme complet. Vous pouvez constater que la classe GregorianCalendar permet 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. 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.

Chapitre 4

Objets et classes

125

Listing 4.1 : CalendarTest.java


import java.text.DateFormatSymbols; import java.util.*; /** * @version 1.4 2007-04-07 * @author Cay Horstmann */ 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); // rcuprer le premier jour de la semaine (dimanche aux US) int firstDayOfWeek = d.getFirstDayOfWeek(); // dterminer lindentation requise pour la premire ligne int indent = 0; while (weekday != firstDayOfWeek) { indent++; d.add(Calendar.DAY_OF_MONTH, -1); weekday = d.get(Calendar.DAY_OF_WEEK); } // afficher les noms des jours String[] weekdayNames = new DateFormatSymbols().getShortWeekdays(); do { System.out.printf("%4s", weekdayNames[weekday]); d.add(Calendar.DAY_OF_MONTH, 1); weekday = d.get(Calendar.DAY_OF_WEEK); } while (weekday != firstDayOfWeek); System.out.println(); for (int i = 1; i <= indent; i++) System.out.print(" "); d.set(Calendar.DAY_OF_MONTH, 1); do { // imprimer la date int day = d.get(Calendar.DAY_OF_MONTH); System.out.printf("%3d", day); // marquer la date du jour avec un *

126

Au cur de Java 2 - Notions fondamentales

if (day == today) System.out.print("*"); else System.out.print(" "); // incrmenter d d.add(Calendar.DAY_OF_MONTH, 1); weekday = d.get(Calendar.DAY_OF_WEEK); // dmarrer une nouvelle ligne au dbut de la semaine if (weekday == firstDayOfWeek) System.out.println(); } 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!= firstDayOfWeek) System.out.println(); } } 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) GregorianCalendar(int year, int month, int day, int hour, int minutes, int seconds)

Construisent un calendrier grgorien la date et lheure spcies. Paramtres : year month day hour minutes seconds

int get(int field)

Lanne de la date. Le mois de la date, base 0 (autrement dit : 0 pour janvier). Le jour du mois. Lheure (de 0 23). Les minutes (de 0 59). Les secondes (de 0 59).

Extrait la valeur du champ spci. 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

Chapitre 4

Objets et classes

127

Calendar.DAY_OF_WEEK Calendar.DAY_OF_WEEK_IN_MONTH Calendar.AM_PM Calendar.HOUR Calendar.HOUR_OF_DAY Calendar.MINUTE Calendar.SECOND Calendar.MILLISECOND Calendar.ZONE_OFFSET Calendar.DST_OFFSET

void set(int field, int value)

Dnit la valeur dun champ particulier. Paramtres :


field

Une des constantes acceptes par get. valueLa nouvelle valeur.

void set(int year, int month, int day) void set(int year, int month, int day, int hour, int minutes, int seconds)

Fournissent de nouvelles valeurs pour les champs. Paramtres : year month day hour minutes seconds

Lanne de la date. Le mois de la date, base 0 (autrement dit : 0 pour janvier). Le jour du mois. Lheure (de 0 23). Les minutes (de 0 59). Les secondes (de 0 59).

void add(int field, int amount)

Est une mthode arithmtique. Elle ajoute la quantit spcie un champ. Par exemple, pour ajouter 7 jours la date courante, utilisez c.add(Calendar.DAY_OF_MONTH, 7). Paramtres : field amount

int getFirstDayOfWeek()

Le champ modier (spci laide dune des constantes acceptes par get). Quantit ajouter au champ (peut tre ngative).

Rcupre le premier jour de la semaine dans le paramtre rgional de lutilisateur en cours, par exemple Calendar.SUNDAY aux Etats-Unis.

void setTime(Date time)

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

128

Au cur de Java 2 - Notions fondamentales

java.text.DateFormatSymbols 1.1

String[] getShortWeekdays() String[] getShortMonths() String[] getWeekdays() String[] getMonths()

Rcuprent les noms des jours ou des mois dans le paramtre rgional en cours. Utilisent les constantes de jour et de mois de Calendar comme valeurs dindice de tableau.

Dnition 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 { constructeur1 constructeur2 . . . mthode1 mthode2 . . . champ1 champ2 . . . }

INFO
Nous avons adopt la rgle qui consiste dnir les mthodes au dbut et placer les champs dinstance la n (dune certaine manire, cela encourage peut-tre lide que linterface doit prendre le pas sur limplmentation).

Considrons cette version trs simplie 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;

Chapitre 4

Objets et classes

129

GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day); hireDay = calendar.getTime(); } // une mthode public String getName() { return name; } // autres mthodes . . . // champs dinstance private String name; private double salary; private Date hireDay; }

Nous analyserons limplmentation de cette classe dans les sections suivantes. Examinez dabord le Listing 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);

Enn, 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 modicateur (ou spcicateur daccs) public. La mthode main avec les instructions que nous venons de dcrire est contenue dans la classe EmployeeTest. Le nom du chier source est EmployeeTest.java, puisque le nom du chier doit tre identique celui de la classe public. Vous ne pouvez avoir quune classe publique dans un chier source, mais le nombre de classes non publiques nest pas limit. Quand vous compilez ce code source, le compilateur cre deux chiers classe dans le rpertoire : EmployeeTest.class et Employee.class.

130

Au cur de Java 2 - Notions fondamentales

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.
Listing 4.2 : EmployeeTest.java
import java.util.*; /** * Ce programme teste la classe Employee * @version 1.11 2004-02-19 * @author Cay Horstmann */ public class EmployeeTest { public static void main(String[] args) { // 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(); }

Chapitre 4

Objets et classes

131

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

Travailler avec plusieurs chiers source


Le programme du Listing 4.2 a deux classes dans un seul chier source. Nombre de programmeurs prfrent avoir un chier source pour chaque classe. Vous pouvez, par exemple, placer la classe Employee dans un chier 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 chiers source correspondants seront compils en chiers de classe. Vous pouvez aussi simplement taper :
javac EmployeeTest.java

Il peut vous paratre surprenant que la seconde possibilit fonctionne, puisque le chier Employee.java nest jamais explicitement compil. Pourtant, lorsque le compilateur Java verra que la classe Employee est utilise dans EmployeeTest.java, il recherchera un chier 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 chier Employee.class, le compilateur Java recompilera automatiquement le chier.
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.

132

Au cur de Java 2 - Notions fondamentales

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

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 modis par nimporte quelle partie du programme. Une telle situation irait compltement lencontre du principe dencapsulation. Toute mthode de toute classe peut modier les champs publics (et, notre avis, certaines parties de code proteront 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.

Chapitre 4

Objets et classes

133

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

Un constructeur porte le mme nom que la classe. Une classe peut avoir plus dun constructeur. 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 sur le tas (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 difcile dtecter. Il faut donc faire attention, dans toutes les mthodes, ne pas utiliser des variables qui soient homonymes des champs dinstance.

134

Au cur de Java 2 - Notions fondamentales

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

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 spcis 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 dnissez gnralement les mthodes en dehors de la classe :
void Employee::raiseSalary(double byPercent) // en C++, pas en Java { . . . }

Si vous dnissez une mthode au sein dune classe, ce sera automatiquement une mthode en ligne :
class Employee { . . . int getName() { return name; } // en ligne en C++ }

Chapitre 4

Objets et classes

135

Dans le langage Java, toutes les mthodes sont dnies 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
Regardons 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; }

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 dni dans le constructeur, aucune mthode ne peut le modier. Nous savons donc que ce champ ne peut jamais tre corrompu. Le champ salary nest pas en lecture seule, mais il ne peut tre modi 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 modier la valeur dun champ dinstance ; vous devez alors fournir trois lments :
m m 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 bnces sont considrables. Il est tout dabord possible de modier 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;

136

Au cur de Java 2 - Notions fondamentales

la mthode getName peut tre modie pour renvoyer :


firstName+" "+lastName

Cette modication 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. 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 { . . . 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.

Chapitre 4

Objets et classes

137

Figure 4.5

Renvoi d'une rfrence 4.5 champ altrable. Figure un

harry = d=

Employee name = salary = hireDay =

Renvoi d'une rfrence un champ altrable.

Date

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

138

Au cur de Java 2 - Notions fondamentales

Pour implmenter une mthode prive en Java, il suft de remplacer le mot cl public par private. En rendant une mthode prive, nous ne sommes plus tenus de la conserver si nous modions limplmentation de la classe. Si la reprsentation interne des donnes est modie, cette mthode pourrait se rvler plus difcile 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 nal


Vous pouvez dnir 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 dnie aprs la n de chaque constructeur. Ensuite, il ne peut plus tre modi. 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 modicateur 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 modie ses objets. Par exemple, la classe String est inaltrable). Pour les classes modiables, le modicateur final risque de jeter la confusion dans lesprit du lecteur. Par exemple,
private final Date hiredate;

signie simplement que la rfrence dobjet stocke dans la variable hiredate nest pas modie aprs la construction de lobjet. Cela ne signie 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 qualie de static. Nous allons maintenant tudier la signication de ce modicateur.

Champs statiques
Si vous dnissez 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 didentication 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; }

Chapitre 4

Objets et classes

139

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 signication de C++.

Implmentons une mthode simple :


public void setId() { id = nextId; nextId++; }

Supposons que vous dnissiez le numro didentication demploy pour harry :


harry.setId();

Le champ id de harry est ensuite dni, et la valeur du champ statique nextId est incrmente :
harry.id = Employee.nextId; Employee.nextId++;

Constantes statiques
Les variables statiques sont plutt rares, mais les constantes statiques sont plus courantes. Par exemple, la classe Math dnit 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 = . . .; . . . }

140

Au cur de Java 2 - Notions fondamentales

Comme nous lavons dj mentionn, il nest jamais souhaitable davoir des champs publics, car tout le monde peut les modier. 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 ux 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 ux 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.

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 :


m

Si une mthode na pas besoin daccder ltat de lobjet, car tous les paramtres ncessaires sont fournis comme paramtres explicites (par exemple, Math.pow).

Chapitre 4

Objets et classes

141

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 signication en C, il dsignait des variables et fonctions globales non accessibles partir dautres chiers. Le mot cl static a t simplement rutilis pour viter den introduire un nouveau. Enn, 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 signication 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 modier 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 . . . } }

142

Au cur de Java 2 - Notions fondamentales

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; 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 du Listing 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 afches. Enn, nous afchons les numros didentication 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.

Chapitre 4

Objets et classes

143

Listing 4.3 : StaticTest.java


/** * Ce programme prsente les mthodes statiques. * @version 1.01 2004-02-19 * @author Cay Horstmann */ 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) { 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; }

144

Au cur de Java 2 - Notions fondamentales

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()); } private private private private } String name; double salary; int id; static int nextId = 1;

Paramtres des mthodes


Revoyons les termes qui dcrivent la manire dont les paramtres peuvent tre passs une mthode (ou une fonction) dans un langage de programmation. Le terme appel par valeur signie que la mthode rcupre exactement la valeur fournie par lappelant. En revanche, un appel par rfrence signie que la mthode obtient lemplacement de la variable fournie par lappelant. Une mthode peut donc modier 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 signie que la mthode obtient une copie de toutes les valeurs de paramtre. En particulier, la mthode ne peut modier le contenu daucun des paramtres qui lui sont passs. Par exemple, dans lappel suivant :
double percent = 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; }

Chapitre 4

Objets et classes

145

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). 3. La mthode se termine et la variable paramtre x nest plus utilise.
Figure 4.6
La modication dun paramtre numrique na pas deffet durable.
Valeur copie

percent = x=

10 30

Valeur triple

Il existe pourtant deux sortes de paramtres de mthode :


m m

les types primitifs (nombres, valeurs boolennes) ; les rfrences des objets.

Vous avez vu quil tait impossible pour une mthode de modier 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 :

146

Au cur de Java 2 - Notions fondamentales

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).
Figure 4.7
La modication dun paramtre objet a un effet durable.
Rfrence copie Salaire tripl

harry = x=

Employee

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) afrment 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 modie 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 :

Chapitre 4

Objets et classes

147

// x fait rfrence Alice, y Bob Employee temp = x; x = y; y = temp; // maintenant x fait rfrence Bob, y Alice

Mais, en n 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=
y=

Employee

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 modier un paramtre de type primitif (cest--dire des nombres ou des valeurs boolennes). Une mthode peut modier ltat dun paramtre objet. Une mthode ne peut pas modier un paramtre objet pour quil fasse rfrence un nouvel objet.

m m

Le programme du Listing 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

148

Au cur de Java 2 - Notions fondamentales

Il parvient ensuite tripler le salaire dun employ :


Testing tripleSalary: Before: salary=50000.0 End of method: salary=150000.0 After: salary=150000.0

Aprs appel la mthode, ltat de lobjet auquel harry fait rfrence a chang. Cela est possible, car la mthode en a modi ltat par lintermdiaire dune copie de la rfrence dobjet. Enn, 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 modient leurs paramtres de rfrence.

Listing 4.4 : ParamTest.java


/** * Ce programme montre le transfert de paramtres en Java. * @version 1.00 2000-01-27 * @author Cay Horstmann */ 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());

Chapitre 4

Objets et classes

149

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

150

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

Construction dun objet


Nous avons vu comment crire des constructeurs simples qui dnissent 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 spcier 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 publiques 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.

Chapitre 4

Objets et classes

151

Initialisation des champs par dfaut


Si vous ne dnissez 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 difcile un tiers de comprendre votre code si les variables sont initialises de manire invisible.
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 le Listing 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.

152

Au cur de Java 2 - Notions fondamentales

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

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 dnir ltat initial des champs dinstance de vos classes. Il est toujours souhaitable de sassurer que, indpendamment de lappel au constructeur, chaque champ dinstance est dni une valeur signicative. Vous pouvez simplement affecter une valeur tous les champs dans la dnition 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 dnir 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 dnis 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++

Chapitre 4

Objets et classes

153

name(n), salary(s), hireDay(y, m, d)

{ }

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 signient les paramtres n et s. Certains programmeurs prxent 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 prxer les champs dinstance avec un caractre de soulignement (_) ou une lettre xe. 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 signication pour ce mot cl.

154

Au cur de Java 2 - Notions fondamentales

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) { // 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 m

en spciant 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 private private ... // bloc { int id; String name; double salary; dinitialisation dobjet

Chapitre 4

Objets et classes

155

id = nextId; nextId++; } }

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 dnition des champs dans les blocs dinitialisation est autorise, mme sils ne sont dnis 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 Java SE 1.4.1. Or, pour viter des dnitions 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 sufsamment complexes pour tromper limplmenteur, nous vous conseillons de placer les blocs dinitialisation aprs les dnitions de champs.

Avec toutes ces techniques dinitialisation, il est difcile 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 spciant 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); }

156

Au cur de Java 2 - Notions fondamentales

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 dnis 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 afche "Hello, World", et cest seulement ensuite que vous obtenez un affreux message derreur vous signalant que main nest pas dnie. Vous pouvez lviter en appelant System.exit(0) la n du bloc dinitialisation statique.

Le programme du Listing 4.5 montre plusieurs des fonctionnalits abordes dans cette section :
m m m m m 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.

Listing 4.5 : ConstructorTest.java


import java.util.*; /** * Ce programme prsente la construction dobjet. * @version 1.01 2004-02-19 * @author Cay Horstmann */ 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

Chapitre 4

Objets et classes

157

for (Employee e: staff) System.out.println("name="+e.getName() +",id="+e.getId() +",salary="+e.getSalary()); } } class Employee { // 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); }

158

Au cur de Java 2 - Notions fondamentales

// bloc dinitialisation dobjet { id = nextId; nextId++; } } java.util.Random 1.0

Random()

Construit un nouveau gnrateur de nombres alatoires.


int nextInt(int n) 1.2

Renvoie un nombre alatoire entre 0 et n-1.

Destruction des objets et mthode nalize


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 chier 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 nalisation 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 ni de lutiliser, vous devez effectuer cette libration manuellement. Ajoutez une mthode dispose ou close que vous appellerez pour nettoyer ce qui doit ltre. De mme, si vous utilisez une classe qui possde une de ces mthodes, 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.

Chapitre 4

Objets et classes

159

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 conit. En fait, pour sassurer vraiment que le nom dun package est unique, Sun recommande dutiliser comme prxe le nom du domaine 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. 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 spcie, il nest plus ncessaire de donner aux classes leur nom complet. Vous pouvez importer une classe spcique ou lensemble dun package. Vous placez les instructions import en tte de vos chiers 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 prxe du package. Il est galement possible dimporter une classe spcique 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.

160

Au cur de Java 2 - Notions fondamentales

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 spciques 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 prxe java. Le plus souvent, vous importez simplement les packages dont vous avez besoin, sans autre proccupation. La seule circonstance demandant une attention particulire est le conit 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 spcique :
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 chiers 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 chier part celui quil compile et les chiers den-ttes explicitement spcis. Le compilateur Java, pour sa part, cherchera dans dautres chiers 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++.

Chapitre 4

Objets et classes

161

Imports statiques
Depuis Java SE 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.*;

en haut de votre chier source, vous pouvez utiliser les mthodes et les champs statiques de la classe System, sans prxe 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 spcique :


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

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

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 chier source, avant le code qui dnit les classes dans le package. Par exemple, le chier Employee.java dans le Listing 4.7 commence ainsi :
package com.horstmann.corejava; public class Employee { . . . }

Si vous ne mettez pas une instruction package dans le chier source, les classes dans ce chier 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 chiers source dun package dans un sous-rpertoire correspondant au nom complet du package. Par exemple, tous les chiers source dans le package com.horstmann.corejava

162

Au cur de Java 2 - Notions fondamentales

doivent se trouver dans le sous-rpertoire com/horstmann/corejava (com\horstmann\corejava sous Windows). Le compilateur place les chiers de classe dans la mme structure de rpertoire. Le programme des Listings 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. Le chier Employee.java doit donc se trouver dans un sous-rpertoire com/horstmann/corejava. En dautres termes, la structure de rpertoire est la suivante :
. (base directory) 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 chier 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) :
. (base directory) 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 chiers (avec les sparateurs de chiers et une extension .java), tandis que linterprteur Java charge une classe (avec des sparateurs par point).
ATTENTION
Le compilateur ne vrie pas les rpertoires lorsquil compile les chiers source. Supposons, par exemple, que vous ayez un chier source commenant par la directive :
package com.mycompany;

Vous pouvez compiler le chier, 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.

Chapitre 4

Objets et classes

163

Listing 4.6 : PackageTest.java


import com.horstmann.corejava.*; // La classe Employee est dfinie dans ce package import static java.lang.System.*; /** * Ce programme montre lutilisation des packages. * @author Cay * @version 1.11 2004-02-19 * @author Cay Horstmann */ public class PackageTest { 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); harry.raiseSalary(5); // du fait de linstruction import, nous navons pas // besoin dutiliser System.out ici out.println("name="+harry.getName() +",salary="+harry.getSalary()); } }

Listing 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 /* * @version 1.10 1999-12-18 * @author Cay Horstmann */ 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(); }

164

Au cur de Java 2 - Notions fondamentales

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

Visibilit dans un package


Nous avons dj rencontr les modicateurs 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 dnit. Si vous ne spciez pas de modicateur public ou private, un composant (classe, mthode ou variable) est accessible toutes les mthodes du mme package. Revenons au Listing 4.2. La classe Employee ntait pas dnie 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. Cellesci doivent maintenant tre explicitement spcies 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 signie 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 modicateur private.

Chapitre 4

Objets et classes

165

INFO
Il est surprenant de constater que ce problme na jamais t corrig, bien que nous layons signal dans huit 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 modie 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 chier 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 rednir le message davertissement (voir Figure 4.9).
Figure 4.9
Changement du message davertissement dans la fentre dun applet.

A partir de la version 1.2, les concepteurs du JDK ont modi le chargeur de classe pour explicitement dsactiver le chargement de classes dnies par lutilisateur, et dont le nom de package commencerait par "java."! Bien entendu, vos propres classes ne bncient 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 chier JAR qui contient des packages plombs.

Le chemin de classe
Vous avez vu que les classes taient stockes dans des sous-rpertoires du systme de chiers. Le chemin daccs de la classe doit correspondre au nom du package. Vous pouvez aussi employer lutilitaire JAR pour ajouter des chiers de classe une archive. Une archive contient plusieurs chiers de classe et des sous-rpertoires dans un chier compress, ce qui conomise lespace et rduit le temps daccs (nous tudierons les chiers JAR plus en dtail au

166

Au cur de Java 2 - Notions fondamentales

Chapitre 10). Lorsque vous utilisez une bibliothque tierce dans vos programmes, vous recevez gnralement un ou plusieurs chiers JAR inclure. Le JDK propose galement plusieurs chiers JAR, comme jre/lib/rt.jar, qui contient des milliers de classes de bibliothque.
ASTUCE
Les chiers JAR ont recours au format ZIP pour lorganisation des chiers et sous-rpertoires. Vous pouvez utiliser nimporte quel utilitaire ZIP pour explorer rt.jar et les autres chiers JAR.

Pour rendre vos classes accessibles aux programmes, vous devez : 1. Placer vos classes lintrieur dun rpertoire, 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 chier Employee.class doit tre localis dans le sous-rpertoire /home/user/classdir/com/horstmann/corejava. 2. Placer les chiers JAR dans un rpertoire, par exemple, /home/user/archives. 3. Dnir le chemin de classe (classpath). Ce chemin est la collection de tous les emplacements pouvant contenir des chiers de classe. Sous UNIX, les lments dans le chemin de classe sont spars par des caractres deux-points :
/home/user/classdir:.:/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 m m

le rpertoire de base /home/user/classdir ou c:\classdir; le rpertoire courant (.) ; le chier JAR /home/user/archives/archive.jar ou c:\archives\archive.jar.

Depuis Java SE 6, vous pouvez utiliser un caractre gnrique pour un rpertoire de chier JAR, comme :
/home/user/classdir:.:/home/user/archives/*

ou
C:\classdir;.;c:\archives\*

Sous Unix, le * doit tre chapp pour viter lexpansion du shell. Tous les chiers JAR ( lexception des chiers .class) du rpertoire archives gurent dans ce chemin de classe. Les classes recherchent toujours les chiers de bibliothque dexcution (rt.jar et les autres chiers JAR dans les rpertoires jre/lib et jre/lib/ext, mais pas les chiers .class) ; vous ne les incluez pas explicitement dans le chemin de classe.

Chapitre 4

Objets et classes

167

ATTENTION
Le compilateur javac recherche toujours les chiers 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 dnition de chemin de classe, cela ne pose pas de problme, le chemin de classe par dfaut est le rpertoire ".". Si vous avez dni le chemin de classe et oubli dinclure le rpertoire ".", vos programmes seront compils sans erreur, mais ils ne pourront pas sexcuter.

Le chemin de classe recense tous les rpertoires et les chiers archives constituant des points de dpart pour retrouver les classes. Voici un exemple de chemin de classe :
/home/user/classdir:.:/home/user/archives/archive.jar

Supposons que la machine virtuelle recherche le chier de la classe com.horstmann.corejava.Employee. Elle va dabord chercher dans les chiers de classe systme qui sont stocks dans les rpertoires jre/lib et jre/lib/ext. Elle ny trouvera pas le chier de classes et va donc se tourner vers le chemin de classe et rechercher les chiers suivants :
m m 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.

La tche du compilateur est plus difcile que celle de la machine virtuelle en ce qui concerne la localisation de chiers. Si vous faites rfrence une classe sans spcier 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 chier source contienne les directives
import java.util.*; import com.horstmann.corejava.*;

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 chiers source pour voir si la source est plus rcente que le chier de classe. Si oui, le chier source est recompil automatiquement. Souvenez-vous que vous ne pouvez quimporter des classes publiques des autres packages. Un chier source peut seulement contenir une classe publique, et les noms du chier et de la classe publique doivent correspondre. Le compilateur peut donc facilement localiser les chiers source pour les classes publiques. Vous pouvez importer des classes non publiques partir des packages courants. Ces classes peuvent tre dnies dans des chiers source avec des noms diffrents. Si vous importez une classe partir du package courant, le compilateur examine tous les chiers source de ce package pour vrier celui qui la dnit.

168

Au cur de Java 2 - Notions fondamentales

Dnition du chemin de classe


Mieux vaut spcier le chemin de classe avec loption -classpath (ou -cp) :
java classpath /home/user/classdir:.:/home/user archives/archive.jar MyProg.java

ou
java classpath c:\classdir;.;\archives\archive.jar MyProg.java

Lensemble de la commande doit tre tap sur une seule ligne. Mieux vaut galement placer cette longue ligne de commande dans un script de shell ou un chier de lot. La mthode prfre pour dnir le chemin de classe consiste employer loption -classpath. Vous pouvez aussi dnir la variable denvironnement CLASSPATH, selon votre shell. Avec le shell Bourne Again (bash), utilisez la commande :
export CLASSPATH=/home/user/classdir:.:/home/user/archives/archive.jar

Avec le shell C, utilisez la commande :


setenv CLASSPATH /home/user/classdir:.:/home/user/archives/archive.jar

Avec le shell Windows, utilisez :


set CLASSPATH=c:\classdir;.;c:\archives\archive.jar

Le chemin de classe est dni tant que le shell existe.


ATTENTION
Certains conseillent de dnir la variable denvironnement CLASSPATH de manire permanente. Or, cest peu recommand. Les programmeurs oublient le paramtre global, puis sont surpris du fait que leurs classes ne se chargent pas correctement. Un exemple particulirement fcheux concerne linstallateur QuickTime dApple sous Windows. Il dnit CLASSPATH globalement de manire quil pointe vers un chier JAR dont il a besoin mais il ninclut pas le rpertoire courant dans le chemin de classe. En consquence, de nombreux programmeurs Java stonnent de voir leurs programmes se compiler mais refuser de sexcuter.

ATTENTION
Certains recommandent de contourner totalement le chemin de classe, en dposant tous les chiers JAR dans le rpertoire jre/lib/ext. Le conseil est vritablement inoprant, et ce pour deux raisons. Les archives qui chargent manuellement dautres classes ne fonctionnent pas correctement lorsquelles sont places dans le rpertoire dextension (voir le Chapitre 9 du Volume II pour en savoir plus sur les chargeurs de classe). De plus, les programmeurs ont tendance oublier les chiers quils y ont placs quelques mois auparavant. Ils sinterrogent alors lorsque le chargeur de classe semble ignorer un chemin particulirement bien conu, chargeant en fait des classes oublies depuis longtemps dans le rpertoire dextension.

Commentaires pour la documentation


Le JDK contient un outil trs utile, appel javadoc, qui gnre une documentation au format HTML partir de vos chiers 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.

Chapitre 4

Objets et classes

169

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 chier spar, le code et les commentaires divergeront fatalement un moment donn. Mais puisque les commentaires de documentation sont dans le mme chier 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 m m 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 xe, <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 chiers comme des images (par exemple des diagrammes ou des images de composants de linterface utilisateur), placez ces chiers dans des sous-rpertoires du rpertoire contenant le chier source appel doc-files. Lutilitaire javadoc copiera ces rpertoires doc-files et leur contenu du rpertoire source vers le rpertoire documentation. Vous devez utiliser le rpertoire doc-files dans votre lien, comme <img src="doc-files/uml.png" alt="UML diagram"/>.

Commentaires de classe
Un commentaire de classe doit tre plac aprs les instructions import, juste avant la dnition 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,

170

Au cur de Java 2 - Notions fondamentales

* Trfle ou Carreau) et une valeur (1 = As, 2 . . . 10, 11 = Valet, * 12 = Dame, 13 = Roi). */ public class Card { . . . }

INFO
Il nest pas ncessaire de commencer chaque ligne 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) */

Toutefois, la plupart des diteurs de texte ajoutent automatiquement les astrisques et les placent aux sauts de ligne.

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

Chapitre 4

Objets et classes

171

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

172

Au cur de Java 2 - Notions fondamentales

Si la balise @see est suivie dun caractre <, vous devez spcier 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 spcier 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 safche 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 chiers source Java, dlimits par les commentaires de documentation /** . . . */. Toutefois, pour gnrer des commentaires de package, vous devez ajouter un chier spar dans chaque rpertoire de package. Deux choix soffrent vous : 1. Fournir un chier HTML intitul package.html. Tout texte entre les balises <body>...</body> est extrait. 2. Fournir un chier Java nomm package-info.java. Le chier doit contenir un commentaire Javadoc initial, dlimit par /** et */ et suivi dune instruction package. Il ne doit contenir aucun autre code ou commentaire. Vous pouvez aussi fournir un commentaire densemble pour tous les chiers source. Placez-le dans un chier appel overview.html, situ dans le rpertoire parent qui contient tous les chiers source. Tout le texte entre les balises <body>...</body> est extrait. Ce commentaire de vue densemble safche lorsque lutilisateur slectionne "Overview" dans la barre de navigation.

Extraction des commentaires


Ici, docDirectory est le nom du rpertoire o vous voulez stocker les chiers HTML. Voici les tapes suivre : 1. Positionnez-vous dans le rpertoire contenant les chiers 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 chier overview.html, le cas chant). 2. Excutez la commande
javadoc -d docDirectory nomDuPackage

pour un package simple.

Chapitre 4

Objets et classes

173

Ou excutez
javadoc -d docDirectory nomDuPackage1 nomDuPackage2...

pour documenter plusieurs packages. Si vos chiers se trouvent dans le package par dfaut, excutez plutt
javadoc -d docDirectory *.java

Si vous avez omis loption -d docDirectory, les chiers 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/javase/6/docs/api *.java

toutes les classes de la bibliothque standard sont automatiquement lies la documentation du site Web de Sun. Avec loption -linksource, chaque chier source est converti au format HTML (sans codage couleur, mais avec des numros de ligne) et chaque nom de classe et de mthode se transforme en lien hypertexte pointant vers la source. Pour dcouvrir dautres options, vous pouvez consulter la documentation en ligne de lutilitaire javadoc ladresse http://java.sun.com/javase/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 spcique 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 chiers 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 gure 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,

174

Au cur de Java 2 - Notions fondamentales

une modication 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 ez pas aveuglment aux valeurs par dfaut ; initialisez explicitement les variables en spciant leur valeur par dfaut, soit dans la classe, soit dans tous les constructeurs. 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 modier. 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 modication 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 modier le salaire dun employ. En revanche, une fois lobjet construit, il nest pas ncessaire de modier sa date dembauche. Bien souvent, les objets contiennent des champs dinstance qui ne doivent pas tre lus ni modis par dautres par exemple, le tableau des codes postaux dans une classe Address. 5. Utilisez un format standard pour la dnition 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.

Chapitre 4

Objets et classes

175

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, protez de loccasion (mais nexagrez pas, la complexit renatra si vous crez trop de petites sous-classes). 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 signicatifs 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 prxe en minuscules : get pour les mthodes daccs (getSalary) et set pour les mthodes daltration (setSalary).

176

Au cur de Java 2 - Notions fondamentales

Ce chapitre a trait des bases des objets et des classes, faisant de Java un langage "bas sur les objets". Pour quil soit vritablement orient objet, un langage de programmation doit aussi prendre en charge lhritage et le polymorphisme. La prise en charge de ces caractristiques par Java fait lobjet du prochain chapitre.

5
Lhritage
Au sommaire de ce chapitre

Classes, superclasses et sous-classes Object : la superclasse cosmique Listes de tableaux gnriques Enveloppes dobjets et autoboxing Mthodes ayant un nombre variable de paramtres Enumration de classes Rexion 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 rexion, la capacit den savoir plus au sujet des classes et de leurs proprits dans un programme en cours dexcution. La rexion 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.

178

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 dnir 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 ambeau de lhritage. Voici comment dnir une classe Manager qui hrite de la classe Employee. Le mot cl extends est employ en Java pour signier 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 signie 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 prxes 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.

Chapitre 5

Lhritage

179

Notre classe Manager a un nouveau champ pour stocker le bonus et une nouvelle mthode pour le dnir :
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 dnies 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 dnies 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 dnition dune sous-classe par extension de sa superclasse, il vous suft 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 }

180

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 signie 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 innie 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.

Chapitre 5

Lhritage

181

Enn, 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 signication 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, il doit les initialiser 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 signications : dnir une rfrence au paramtre implicite, et appeler un autre constructeur de la mme classe. Le mot cl super a galement deux signications : 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 rednition 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 dnissons son bonus :
Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15); boss.setBonus(5000);

182

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

Afchons le salaire de chacun :


for (Employee e: staff) System.out.println(e.getName()+" " +e.getSalary());;

Cette boucle afche les donnes suivantes :


Carl Cracker 85000.0 Harry Hacker 50000.0 Tommy Tester 40000.0

staff[1] et staff[2] afchent 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, soit Manager. 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).

Le Listing 5.1 contient un programme qui montre la faon dont diffre le calcul du salaire pour les objets Employee et Manager.

Chapitre 5

Lhritage

183

Listing 5.1 : ManagerTest.java


import java.util.*; /** * Ce programme prsente lhritage. * @version 1.21 2004-02-21 * @author Cay Horstmann */ 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() {

184

Au cur de Java 2 - Notions fondamentales

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

Chapitre 5

Lhritage

185

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(. . .); e = new Manager(. . .); // objet Employee attendu // 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 le Listing 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 seulement comme un objet Employee. Cela signie que vous pouvez appeler
boss.setBonus(5000); // OK

186

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

Chapitre 5

Lhritage

187

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 dnissez 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 Java SE 5.0, les types de retours devaient tre identiques. Mais la sous-classe peut maintenant modier 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 Java SE 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 modicateur 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 dnit 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 donc 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, elle 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.

188

Au cur de Java 2 - Notions fondamentales

Nous allons examiner ce processus en dtail dans lappel e.getSalary() du Listing 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 dnies 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 rednie 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. Enn, la machine virtuelle appelle la mthode. La liaison dynamique possde une proprit trs importante : elle rend les programmes extensibles sans quil soit ncessaire de modier 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 spcicateur public pour la mthode de la sous-classe. Le compilateur proteste alors et signale que vous essayez de fournir un privilge daccs plus faible.

Chapitre 5

Lhritage

189

Empcher lhritage : les classes et les mthodes nal


Dans certaines circonstances, vous souhaiterez interdire dautres programmeurs de former une sous-classe partir dune des classes que vous avez cres. On emploie le modicateur final pour spcier 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 suft pour cela dutiliser le modicateur 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 qualis de final. Un champ nal ne peut pas tre modi 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 : vrier 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 signie que personne ne peut dnir 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

190

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 vrier 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 spcier 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 justie 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 spcique 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 spciques. 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 dnir 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).

Chapitre 5

Lhritage

191

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 vrie 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 n, 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 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 spcique 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 suft 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.

192

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

Chapitre 5

Lhritage

193

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 :
abstract class Person { public Person(String n) { name = n; } public abstract String getDescription(); public String getName() { return name; } private String name; }

ASTUCE
Certains programmeurs ne pensent pas que les classes abstraites peuvent avoir des mthodes concrtes. Il est toujours prfrable de dplacer les champs et mthodes communs (abstraits ou non) dans la superclasse (abstraite ou non).

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 indnies. La sous-classe doit ensuite tre qualie dabstraite galement. Vous pouvez aussi dnir toutes les mthodes, la sous-classe nest alors plus abstraite.

194

Au cur de Java 2 - Notions fondamentales

Nous allons par exemple, dnir 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 dnir 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; }

La classe Student dnit la mthode getDescription. Toutes les mthodes de la classe Student sont donc concrtes et la classe nest plus dsormais abstraite.

Chapitre 5

Lhritage

195

Le programme du Listing 5.2 dnit 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 afchons 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 indnie ? 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 dnie. Auriez-vous pu omettre totalement la mthode abstraite pour la superclasse Person et simplement dnir 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.
Listing 5.2 : PersonTest.java
import java.util.*; /** * Ce programme prsente les classes abstraites * @version 1.01 2004-02-21 * @author Cay Horstmann */ 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()); } }

196

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 { /** * @param n Nom de ltudiant * @param m La spcialit de ltudiant */

Chapitre 5

Lhritage

197

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, les champs protected doivent tre utiliss 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 modiez 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.
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 signication de protected est lgrement diffrente en C++, et la notion de protection en Java est encore moins sre quen C++.

198

Au cur de Java 2 - Notions fondamentales

Rsumons les caractristiques des quatre modicateurs 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 modicateur nest spci. Visible par tout le package.

Object : la superclasse cosmique


La classe Object reprsente lanctre ultime toutes les classes Java tendent Object. Nanmoins, vous navez jamais crire :
class Employee extends Object

La superclasse Object est prise en compte par dfaut si aucune superclasse nest explicitement spcie. Comme chaque classe Java tend 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 Volume II 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*.

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 vrie que les deux rfrences dobjet sont identiques. Cest un dfaut assez raisonnable : si deux objets sont identiques, ils doivent certainement tre gaux. Cela suft pour certaines classes. Il est peu logique, par exemple, de comparer deux objets PrintStream des ns dgalit. Vous voudrez pourtant souvent implmenter le test dgalit dans lequel deux objets sont considrs gaux lorsquils ont le mme tat.

Chapitre 5

Lhritage

199

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 identiants. 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 dnissez 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; } }

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

200

Au cur de Java 2 - Notions fondamentales

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 spcication du langage Java requiert que la mthode equals ait les proprits suivantes : 1. Quelle soit rective : 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 les mmes name, salary et hireDate. Si Employee.equals utilise un test instanceof, cet appel renvoie true. Mais cela signie 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 spciques 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. 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. Toutefois, lexemple des ensembles est plutt spcialis. Il serait logique de dclarer AbstractSet.equals sous la forme final, car personne ne doit rednir la smantique de lgalit du jeu (la mthode nest pas rellement final. Ceci permet une sous-classe dimplmenter un algorithme plus efcace pour le test dgalit).

Chapitre 5

Lhritage

201

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 xe 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.
INFO
La bibliothque Java standard contient plus de 150 implmentations des mthodes equals, avec un mli-mlo dutilisations dinstanceof, dappels getClass, dinterceptions de ClassCastException ou de rien du tout.

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. Vriez 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 vrier 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;

4. 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;

5. Convertissez otherObject en une variable du type de votre classe :


nomDeClasse other = (nomDeClasse)otherObject

6. 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) && . . .;

202

Au cur de Java 2 - Notions fondamentales

Si vous rednissez equals dans une sous-classe, incluez un appel super.equals(other).


ASTUCE
Si vous disposez de champs de type tableau, vous pouvez utiliser la mthode Arrays.equals pour vrier que les lments de tableau correspondants sont gaux.

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 dnit une mthode nayant aucun rapport. Depuis Java SE 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 dnissiez 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 est signale car cette mthode ne remplace aucune mthode de la superclasse Object.

java.util.Arrays 1.2

static boolean equals(type[] a, type[] b) 5.0

Renvoie true si les tableaux ont des longueurs gales et des lments gaux des positions correspondantes. Les tableaux peuvent avoir des types de composants Object, int, long, short, char, byte, boolean, float ou double.

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() soient diffrents. Le Tableau 5.1 reprend quelques exemples de codes de hachage tirs de la mthode hashCode de la classe String.

Chapitre 5

Lhritage

203

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

Tableau 5.1 : Codes de hachage tirs de la fonction hashCode

Chane Hello Harry Hacker

Code de hachage 69609650 69496448 2141031506

La mthode hashCode est dnie 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 StringBuilder(s); System.out.println(s.hashCode() + " " + sb.hashCode()); String t = new String("Ok"); StringBuilder tb = new StringBuilder(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 constructeurs de chanes

Objet s sb t tb

Code de hachage 2556 20526976 2556 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 constructeurs de chanes sb et tb disposent de codes de hachage diffrents car aucune mthode hashCode na t dnie pour la classe StringBuilder et la mthode hashCode par dfaut de la classe Object drive le code de hachage de ladresse mmoire de lobjet. Si vous rednissez la mthode equals, vous devrez aussi rednir 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 II). 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.

204

Au cur de Java 2 - Notions fondamentales

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 dnitions 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 dnissez 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.
ASTUCE
Si vous disposez de champs de type tableau, vous pouvez utiliser la mthode Arrays.hashCode pour calculer un code de hachage compos des codes de hachage des lments du tableau.

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.
java.util.Arrays 1.2

static int hashCode(type[] a) 5.0

Calcule le code de hachage du tableau a, pouvant avoir un type de composant Object, int, long, short, char, byte, boolean, float ou double.

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

Chapitre 5

Lhritage

205

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 +",hireDay="+hireDay +"]"; }

La mthode toString sapplique alors galement aux sous-classes. Bien entendu, le programmeur de la sous-classe doit dnir 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 safche 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 de chane 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 afche la chane rsultante.

206

Au cur de Java 2 - Notions fondamentales

La classe Object dnit la mthode toString pour afcher 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.
ATTENTION
Malheureusement, les tableaux hritent la mthode toString de Object; en outre, le type de tableau qui safche dans un format archaque, par exemple :
int[] luckyNumbers = { 2, 3, 5, 7, 11, 13 }; String s = "" + luckyNumbers;

donne la chane "[I@1a46e30" (le prxe [I indique un tableau dentiers). La solution consiste appeler la mthode Arrays.toString statique la place. Le code
String s = Arrays.toString(luckyNumbers);

produit la chane "[2, 3, 5, 7, 11, 13]". Pour afcher correctement les tableaux multidimensionnels (cest--dire des tableaux de tableaux), utilisez

Arrays.deepToString.

La mthode toString est un outil prcieux de consignation. De nombreuses classes de la bibliothque de classes standard dnissent la mthode toString comme fournisseur dinformations utiles sur ltat dun objet. Cela 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 du Listing 5.3 implmente les mthodes equals, hashCode et toString pour les classes Employee et Manager.
Listing 5.3 : EqualsTest.java
import java.util.*; /** * Ce programme prsente la mthode equals. * @version 1.11 2004-02-21 * @author Cay Horstmann */

Chapitre 5

Lhritage

207

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

208

Au cur de Java 2 - Notions fondamentales

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

Chapitre 5

Lhritage

209

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

210

Au cur de Java 2 - Notions fondamentales

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 xe 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 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 spcier 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 modication dynamique des tableaux. Une fois que la taille dun tableau est spcie, il nest pas facile de la changer. La manire la plus simple de grer cette situation courante consiste utiliser une autre classe Java, appele 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 Java SE 5.0, ArrayList est une classe gnrique avec un paramtre de type. Pour spcier 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 dnir 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 Java SE 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 sufxes de type <...>. Vous pouvez continuer utiliser ArrayList sans sufxe <...> dans Java SE 5.0 et versions ultrieures. On le considre comme un type "brut", dont le paramtre de type est effac.

Chapitre 5

Lhritage

211

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 efcace, 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 nira 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);

Cet appel alloue un tableau interne de 100 objets. Les 100 premiers appels add nimpliquent aucune raffectation coteuse. 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 de rallocations 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 dnitive, vous pouvez appeler la mthode trimToSize. Elle ajuste la taille du bloc de mmoire pour que la quantit

212

Au cur de Java 2 - Notions fondamentales

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.


ArrayList<T>(int initialCapacity)

Construit une liste de tableaux vide ayant la capacit spcie. Paramtres :

initialCapacity La capacit de stockage initiale de la liste de tableaux.

boolean add(T obj)

Ajoute un lment la n de la liste de tableaux. Renvoie toujours true. Paramtres :

int size()

obj

Llment ajouter.

Renvoie le nombre dlments actuellement contenus dans la liste de tableaux (bien entendu, elle est toujours infrieure ou gale la capacit de la liste de tableaux).

void ensureCapacity(int capacity)

Vrie 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 modier, il faut appeler les mthodes get et set.

Chapitre 5

Lhritage

213

Par exemple, pour dnir 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).
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.

Pour obtenir un lment de liste de tableau, utilisez


Employee e = staff.get(i);

ce qui est quivalent :


Employee e = a[i];

INFO
Avant Java SE 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 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.

Une petite astuce permet de proter dun double avantage une croissance exible 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);

214

Au cur de Java 2 - Notions fondamentales

Au lieu dajouter des lments la n dune liste de tableaux, vous pouvez aussi les insrer au milieu. Utilisez la mthode add avec un paramtre dindice :
int n = staff.size() / 2; staff.add(n, e);

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 efcaces. 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 13. Depuis Java SE 5.0, vous pouvez utiliser la boucle "for each" pour parcourir le contenu dune liste de tableau :
for (Employee e : staff) faire quelque chose avec e

Cette boucle a le mme effet que


for (int i = 0; i < staff.size(); i++) { Employee e = staff.get(i); faire quelque chose avec e }

Le Listing 5.4 est une modication du programme EmployeeTest du Chapitre 4. Le Tableau Employee[] est remplac par un ArrayList<Employee>. Remarquez les changements suivants :
m m m m

Il nest pas ncessaire de spcier 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].

Listing 5.4 : ArrayListTest.java


import java.util.*; /** * Ce programme prsente la classe ArrayList. * @version 1.1 2004-02-21 * @author Cay Horstmann */ public class ArrayListTest { public static void main(String[] args) { // remplir le tableau staff avec trois objets Employee

Chapitre 5

Lhritage

215

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()); } } 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 spci, ce qui remplace le contenu prcdent.

216

Au cur de Java 2 - Notions fondamentales

Paramtres :

index obj

La position dinsertion, qui doit tre comprise entre 0 et size() -1. La nouvelle valeur

T get(int index)

Rcupre la valeur stocke lindex spci. Paramtres :

index

Lindex de llment rcuprer, qui doit tre compris entre 0 et size() -1.

void add(int index, T obj)

Place un nouvel lment la position spcie, en dcalant les autres lments vers le haut. Paramtres : index obj

T remove(int index)

La position dinsertion, qui doit tre comprise entre 0 et size() -1. Le nouvel lment.

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 types


Lorsque vous crivez un nouveau code avec Java SE 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 type 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 rchissant bien, le comportement est simplement le mme que celui avant Java SE 5.0. Lintgrit de la machine nest donc jamais remise en cause. Dans ce cas, vous ne perdez pas en scurit, mais vous ne protez pas non plus des vrications de compilation.

Chapitre 5

Lhritage

217

A linverse, lorsque vous attribuez un ArrayList brut un ArrayList typ, 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.

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 gnriques dans Java. Pour des raisons de compatibilit, le compilateur traduit toutes les listes de tableaux tapes en objets ArrayList bruts, aprs avoir vri 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 vrications 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 modier 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 efcace 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 lefcacit.

218

Au cur de Java 2 - Notions fondamentales

Une autre innovation de Java SE 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.


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, 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 xes. Par exemple, si a et b avaient t initialiss avec 100 dans lexemple prcdent, la comparaison aurait russi.

Enn, 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.

Chapitre 5

Lhritage

219

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. 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 modier 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 modie les paramtres numriques. Si vous dsirez crire une mthode qui modie des paramtres numriques, vous pouvez utiliser un des types holder dnis dans le package org.omg.CORBA. Il existe des types IntHolder, 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 i, en base 10.


static String toString(int i, int radix)

Permet de renvoyer une reprsentation du nombre i dans la base spcie par le paramtre radix.

220

Au cur de Java 2 - Notions fondamentales

static int parseInt(String s) static int parseInt(String s, int radix)

Renvoient la valeur dentier dont les chiffres sont contenus dans la chane s. La chane doit reprsenter un nombre entier exprim en base 10 (pour la premire mthode) ou dans la base spcie par le paramtre radix (dans la seconde).

static Integer valueOf(String s) static Integer valueOf(String s, int radix)

Renvoient un nouvel objet Integer initialis avec lentier dont les chiffres sont contenus dans la chane s. La chane doit reprsenter un nombre entier exprim en base 10 (pour la premire mthode) ou dans la base spcie par le paramtre radix (pour la seconde).
java.text.NumberFormat 1.1

Number parse(String s)

Renvoie la valeur numrique, en supposant que la chane spcie reprsente un nombre.

Mthodes ayant un nombre variable de paramtres


Avant Java SE 5.0, chaque mthode Java disposait dun nombre xe 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 dnie 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 spcicateur 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[]. 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 %s", new Object[] { new Integer(n), "widgets" } );

Chapitre 5

Lhritage

221

Vous pouvez dnir vos propres mthodes avec des paramtres variables et spcier 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 rednir une fonction existante dont le dernier paramtre est un tableau par une mthode disposant de paramtres variables, sans casser un code existant. MessageFormat.format, par exemple, a t amlior dans ce sens dans Java SE 5.0. Si vous le souhaitez, vous pouvez mme dclarer la mthode main sous la forme :
public static void main(String... args)

Classes dnumration
Vous avez vu au Chapitre 3 comment dnir les types numrs dans Java SE 5.0 et versions ultrieures. Voici un exemple typique :
public enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };

Le type dni 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; }

222

Au cur de Java 2 - Notions fondamentales

private String abbreviation; }

Tous les types numrs sont des sous-classes de la classe Enum, dont ils hritent de plusieurs mthodes. La mthode la plus utile est toString, qui renvoie le nom de la constante numre. Ainsi, Size.SMALL.toString() renvoie la chane "SMALL". Linverse de toString est la mthode statique valueOf. Par exemple, linstruction
Size s = (Size) Enum.valueOf(Size.class, "SMALL");

dnit s sur Size.SMALL. Chaque type numr possde une mthode de valeurs statique qui renvoie un tableau de toutes les valeurs de lnumration. Par exemple, lappel :
Size[] values = Size.values();

renvoie le tableau avec les lments Size.SMALL, Size.MEDIUM, Size.LARGE et Size.EXTRA_LARGE. La mthode ordinal donne la position dune constante numre dans la dclaration enum, en partant de zro. Ainsi, Size.MEDIUM.ordinal() renvoie 1. Le petit programme du Listing 5.5 montre comment fonctionnent les types numrs.
INFO
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>. Le paramtre de type est utilis dans la mthode compareTo. Nous verrons cette mthode au Chapitre 6 et les paramtres de type au Chapitre 12.

Listing 5.5 : EnumTest.java


import java.util.*; /** * Ce programme prsente les types numrs. * @version 1.0 2004-05-24 * @author Cay Horstmann */ 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 _."); } }

Chapitre 5

Lhritage

223

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


int ordinal()

Renvoie la position base sur zro de cette constante numre dans la dclaration enum.
int compareTo(E other)

Renvoie un entier ngatif si cette constante numre arrive avant, zro si this == other, et un entier positif le reste du temps. Lordre des constantes est donn par la dclaration enum.

Rexion
La bibliothque de rexion 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 II pour en savoir plus sur JavaBeans). Au moyen de la rexion, 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 recteur. Le mcanisme de rexion est extrmement puissant. Comme vous le montrent les sections suivantes, il peut tre employ pour :
m 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 ; proter des objets Method qui se comportent comme les pointeurs de fonction des langages tels que C++.

m m

La rexion 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.

224

Au cur de Java 2 - Notions fondamentales

La classe Class
Lorsque le programme est lanc, le systme dexcution de Java gre, pour tous les objets, ce que lon appelle "lidentication 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());

afche
Employee Harry Hacker

si e est un employ ou
Manager Harry Hacker

si e est un directeur. Si la classe se trouve dans un package, le nom du package fait partie du nom de la classe :
Date d = new Date(); Class cl = d.getClass(); String name = cl.getName(); // nom rgl sur "java.util.Date"

Vous pouvez aussi obtenir un objet Class correspondant un nom de classe, 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 vrie. Consultez la section "Introduction linterception des exceptions" 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. Afchez dabord un cran splash, puis forcez manuellement le chargement des autres classes en appelant Class.forName.

Chapitre 5

Lhritage

225

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 Java SE 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() renvoie "[Ljava.lang.Double;".

int[].class.getName() renvoie "[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) . . .

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

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.

226

Au cur de Java 2 - Notions fondamentales

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.

Introduction linterception dexceptions


La gestion des exceptions est traite 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". Le lancement dune exception est plus souple que la terminaison du 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 afche 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 : vries et non vries (checked/unchecked). Dans le cas dexceptions vries, le compilateur vrie que vous avez fourni un gestionnaire. Cependant, de nombreuses exceptions communes, telle quune tentative daccder une rfrence null, ne sont pas vries. Le compilateur ne vrie 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. 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 vrie. Vous tudierez, au Chapitre 11, plusieurs stratgies de gestion dexception. Pour linstant, nous nous contenterons de voir limplmentation du gestionnaire le plus simple. Placez une ou plusieurs instructions pouvant lancer des exceptions vries, 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 }

Chapitre 5

Lhritage

227

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 vries. Il est facile de dterminer les mthodes qui lancent celles-ci le compilateur protestera quand vous appellerez une mthode menaant de lancer une exception vrie et que vous ne fournirez pas de gestionnaire.
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 rexion pour plus dinformations sur la faon de fournir les paramtres.

java.lang.Throwable 1.0 void printStackTrace()

Afche lobjet Throwable et la trace de la pile dans lunit derreur standard.

La rexion pour analyser les caractristiques dune classe


Nous vous proposons un bref aperu des lments les plus importants du mcanisme de rexion, 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 modicateurs spcis, 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.

228

Au cur de Java 2 - Notions fondamentales

Il vous suft dappeler la mthode approprie de Modifier et de lutiliser sur lentier renvoy par getModifiers. Il est galement possible demployer Modifier.toString pour afcher les modicateurs. 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. Le Listing 5.6 explique comment afcher toutes les informations relatives une classe. Le programme vous demande le nom dune classe, puis afche 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 afche :
public 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; }

Chapitre 5

Lhritage

229

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.
Listing 5.6 : ReectionTest.java
import java.util.*; import java.lang.reflect.*; /** * Ce programme utilise la rflexion pour afficher toutes les * caractristiques dune classe. * @version 1.1 2004-02-01 * @author Cay Horstmann */ 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(); String modifiers = Modifier.toString(cl.getModifiers()); if (modifiers.length() > 0) System.out.print(modifiers + " "); 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); }

230

Au cur de Java 2 - Notions fondamentales

/** * affiche tous les constructeurs dune classe * @param cl Une classe */ public static void printConstructors(Class cl) { Constructor[] constructors = cl.getDeclaredConstructors(); for (Constructor c: constructors) { String name = c.getName(); System.out.print(" "); String modifiers = Modifier.toString(c.getModifiers()); If (modifiers.length() > 0) System.out.print(modifiers + " "); 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(); System.out.print(" "); // affiche les modificateurs, le type renvoy // et le nom de la mthode String modifiers = Modifier.toString(m.getModifiers()); if (modifiers.length() > 0) System.out.print( modifiers +" "); 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(");"); } }

Chapitre 5

Lhritage

231

/** * 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(); System.out.print(" "); String modifiers = Modifier.toString(f.getModifiers()); if (modifiers.length() > 0) System.out.println( modifiers + " "); System.out.println(type.getName()+" "+name+";"); } } } java.lang.Class 1.0

Field[] getFields() 1.1 Field[] getDeclaredFields() 1.1

getFields renvoie un tableau contenant les objets Field reprsentant les champs publics de cette classe ou de ses superclasses. 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 dnit 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.

232

Au cur de Java 2 - Notions fondamentales

int getModifiers()

Renvoie un entier dcrivant les modicateurs 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.
java.lang.reflect.Modifier 1.1

static String toString(int modifiers)

Renvoie une chane avec les modicateurs correspondant aux bits dnis 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 modicateur du nom de la mthode.

La rexion 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 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 spcique dun objet dont le nom et le type sont connus lors de lcriture du programme. Mais la rexion 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 :

Chapitre 5

Lhritage

233

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. Par dfaut, le mcanisme de rexion 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 rexion enveloppe automatiquement la valeur du champ dans la classe enveloppe approprie, en loccurrence, Double. Bien entendu, il est possible de modier les valeurs obtenues. Lappel f.set(obj, value) affecte une nouvelle valeur au champ de lobjet obj reprsent par f. Le Listing 5.7 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. Le Listing 5.7 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();

234

Au cur de Java 2 - Notions fondamentales

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); } catch (Exception e) { e.printStackTrace(); } } } r += "]"; cl = cl.getSuperclass(); } while (cl!= null); return r; } . . . }

La totalit du code du Listing 5.7 doit traiter deux complexits. Les cycles de rfrence pourraient entraner une rcursion innie. 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 pour implmenter les mthodes toString de vos propres classes, comme ceci :
public String toString() { return new ObjectAnalyzer().toString(this); }

Il sagit dune technique sre pour crer une mthode toString, qui pourra vous tre utile dans vos programmes.

Chapitre 5

Lhritage

235

Listing 5.7 : ObjectAnalyzerTest.java


import java.lang.reflect.*; import java.util.*; /** * Ce programme utilise une rflexion pour espionner les objets. * @version 1.11 2004-02-21 * @author Cay Horstmann */ 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)); } } 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);

236

Au cur de Java 2 - Notions fondamentales

// 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(); } while (cl!= null); return r; } private ArrayList<Object> visited = new ArrayList<Object>(); } java.lang.reflect.AccessibleObject 1.2 void setAccessible(boolean flag)

Dnit lindicateur daccessibilit de lobjet rexion. La valeur true signie que la vrication 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 rexion.


static void setAccessible(AccessibleObject[] array,boolean flag)

Permet de spcier ltat de lindicateur daccessibilit pour un tableau dobjets.


java.lang.Class 1.1 Field getField(String name) Field[] getFields()

Rcuprent le champ public avec le nom donn ou un tableau de tous les champs.

Field getDeclaredField(String name) Field[] getDeclaredFields()

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

Chapitre 5

Lhritage

237

void set(Object obj, Object newValue)

Dnit le champ dcrit par cet objet Field dans lobjet obj avec une nouvelle valeur.

La rexion 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);

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 sufrait 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 ns, il nous faut connatre la taille (ou longueur) et le type du nouveau tableau.

238

Au cur de Java 2 - Notions fondamentales

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. Conrmer quil sagit bien dun tableau. 3. Utiliser ensuite la mthode getComponentType de la classe Class (qui nest dnie que pour les objets classe reprsentant des tableaux) an 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); 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 ! Le Listing 5.8 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.
INFO
Nous prsentons ce programme pour montrer le fonctionnement des tableaux via la rexion. Pour simplement agrandir un tableau, utilisez la mthode copyOf de la classe Arrays.
Employee[] a = new Employee[100]; . . . // le tableau est plein a = Arrays.copyOf(a, a.length * 11 / 10 + 10);

Listing 5.8 : ArrayGrowTest.java


import java.lang.reflect.*; /** * Ce programme prsente lutilisation de la rflexion * pour manipuler des tableaux

Chapitre 5

Lhritage

239

* @version 1.01 2004-02-21 * @author Cay Horstmann */ 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 * 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; }

240

Au cur de Java 2 - Notions fondamentales

/** * 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 primitif */ 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.

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, an 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 Java 1.1, Java dispose de pointeurs de mthodes, qui sont une consquence (peut-tre accidentelle) du dveloppement du package de rexion.
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.

Chapitre 5

Lhritage

241

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 Java SE 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 Java SE 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 Java SE 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. 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 Java SE 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 du Listing 5.8 afche une table de valeurs pour des fonctions mathmatiques comme Math.sqrt ou Math.sin. Lafchage 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

242

Au cur de Java 2 - Notions fondamentales

6.0000 7.0000 8.0000 9.0000 10.0000

| | | | |

2.4495 2.6458 2.8284 3.0000 3.1623

Le code dafchage dune table est bien sr indpendant de la fonction relle qui safche 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", 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 dnissons 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. Le Listing 5.9 prsente le code complet du tabulateur gnrique et quelques tests.
Listing 5.9 : MethodPointerTest.java
import java.lang.reflect.*; /** * Ce programme montre comment appeler des mthodes via la rflexion. * @version 1.1 2004-02-21 * @author Cay Horstmann */ 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", 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; }

Chapitre 5

Lhritage

243

/** * 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); 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. Les paramtres et le rsultat de la mthode invoke sont ncessairement de type Object. Cela signie que lon doit effectuer un bon nombre de transtypages. En consquence, le compilateur na pas loccasion de vrier votre code et les erreurs napparaissent que durant les tests, lorsquelles sont plus difciles corriger. De plus, une routine qui exploite la rexion pour obtenir un pointeur de mthode est sensiblement plus lente quun code 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.

244

Au cur de Java 2 - Notions fondamentales

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 dnir la plupart des champs dinstance comme protected, "au cas o", est une bonne ide, an 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 rednies 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 lafchage des ches 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);

Chapitre 5

Lhritage

245

Lhritage nest donc pas indiqu dans ce cas. 5. Ne modiez 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 vrier si vos nouvelles dnitions sont adaptes. Vous pouvez "rparer" le problme de la mthode add dans la classe Holiday en rednissant 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 nal, 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 rexion. Le mcanisme de rexion 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 rexion 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.

246

Au cur de Java 2 - Notions fondamentales

Vous avez maintenant vu que Java accepte les bases de la programmation oriente objet : classes, hritage et polymorphisme. Au prochain chapitre, nous aborderons deux sujets avancs trs importants pour utiliser efcacement Java : les interfaces et les classes internes.

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 modier le clone sans affecter loriginal. Nous tudierons ensuite le mcanisme des classes internes. Les classes internes sont techniquement quelque peu complexes elles sont dnies 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.

248

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 signie que toute classe qui implmente linterface Comparable doit possder une mthode compareTo et que la mthode doit accepter un paramtre Object et renvoyer un entier.
INFO
Depuis Java SE 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 dnir 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.

Chapitre 6

Interfaces et classes internes

249

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 m

dclarer que votre classe a lintention dimplmenter linterface donne ; fournir les dnitions pour toutes les mthodes dans linterface.
class Employee implements Comparable

Pour dclarer quune classe implmente une interface, employez le mot cl implements : 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 Java SE 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 identicateur 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 premier ID est infrieur au second, gale 0 sils sont identiques et positive dans les autres cas. Lindication est sufsante ; la plage des entiers doit tre sufsamment petite pour quil ne se produise pas un dpassement lors de la

250

Au cur de Java 2 - Notions fondamentales

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 ottante. La diffrence salary - other.salary peut alors tre arrondie 0 si les salaires sont sufsamment 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 vrier 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 conrme, car chaque classe qui implmente linterface Comparable doit fournir la mthode.
INFO
Il serait logique que la mthode sort dans la classe Arrays soit dnie pour accepter un tableau Comparable[], an 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.

Le Listing 6.1 dcrit la totalit du code ncessaire pour le tri dun tableau demploys.
Listing 6.1 : EmployeeSortTest.java
import java.util.*; /** * Ce programme prsente lutilisation de linterface Comparable. * @version 1.30 2004-02-27 * @author Cay Horstmann */

Chapitre 6

Interfaces et classes internes

251

public class EmployeeSortTest { public static void main(String[] args) { 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; }

252

Au cur de Java 2 - Notions fondamentales

private String name; private double salary; } java.lang.Comparable<T> 1.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.compare To(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();

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

Chapitre 6

Interfaces et classes internes

253

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 vrier si un objet appartient une classe spcique, vous pouvez utiliser instanceof pour vrier 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.
INFO
Il est lgal de qualier 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 spcications du langage Java recommandent de ne pas fournir de mots cls redondants et nous respectons cette recommandation.

254

Au cur de Java 2 - Notions fondamentales

Certaines interfaces dnissent simplement les constantes et pas de mthodes. Par exemple, la bibliothque standard contient une interface SwingConstants qui dnit 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 exibilit pour la dnition 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 dle des objets de votre classe. Supposez, par consquent, que vous vouliez disposer des fonctions de clonage et de comparaison ; il vous sufra 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 de 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

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 efcace (comme dans Eiffel).

Chapitre 6

Interfaces et classes internes

255

Les interfaces, elles, procurent la plupart des avantages de lhritage multiple tout en vitant les risques de complexit et dinefcacit.
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 afrment 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 signie 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 signie 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. Rchissons 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 "supercielle" (shallow) elle ne clone pas les objets qui sont rfrencs lintrieur dautres objets. Le fait que cette copie soit supercielle est-il important ? Cela dpend. Si le sous-objet qui est partag entre loriginal et le clone superciel 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.

256

Au cur de Java 2 - Notions fondamentales

Figure 6.1
Copie et clonage.
original = copy =

Copie Employee

Clonage original = Employee

copy =

Employee

Figure 6.2
Une copie "shallow".

original =

Employee name = salary = hireDay = 50000.0

String

copy =

Employee name = salary = hireDay = 50000.0

Date

Chapitre 6

Interfaces et classes internes

257

Cependant, assez frquemment, les sous-objets sont altrables et vous devez rednir 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. La troisime option est celle par dfaut. Pour pouvoir choisir la premire ou la deuxime option, une classe doit : 1. Implmenter linterface Cloneable. 2. Rednir la mthode clone avec le modicateur daccs public.
INFO
La mthode clone est dclare protected dans la classe Object an 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 rednir 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 spcie 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 vrie (checked exception), si un objet requiert le clonage, mais nimplmentent 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 spcique. 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 les interfaces de balisage dans vos programmes.

Mme si limplmentation par dfaut (copie supercielle) de clone est adquate, vous devez toujours implmenter linterface Cloneable, rednir clone comme public, appeler super.clone(). Voici un exemple :
class Employee implements Cloneable {

258

Au cur de Java 2 - Notions fondamentales

// lever le niveau de visibilit public, // changer le type de retour public Employee clone() throws CloneNotSupportedException { Return (Employee) super.clone(); } . . . }

INFO
Avant Java SE 5.0, la mthode clone avait toujours un type de retour Object. Les types de retours covariants de Java SE 5.0 vous permettent de spcier 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 Employee 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(); } catch (CloneNotSupportedException e) { return null; } // ceci narriverait pas, puisque nous utilisons Cloneable }

Chapitre 6

Interfaces et classes internes

259

Ceci convient bien pour les classes final. Dans les autres cas, il vaut mieux laisser le spcicateur 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 dni 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 xe 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 du Listing 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 dnie pour faire une copie intgrale.
INFO
Tous les types de tableaux possdent une mthode clone qui est public et non protected. Vous pouvez lutiliser pour crer un nouveau tableau qui contiendra des copies de tous les lments. Par exemple,
int[] luckyNumbers = { 2, 3, 5, 7, 11, 13 }; int[] cloned = (int[]) luckyNumbers.clone(); cloned[5] = 12; // ne modifie pas luckyNumbers[5]

INFO
Le Chapitre 1 du Volume II 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 efcace.

Listing 6.2 : CloneTest.java


import java.util.*; /** * Ce programme prsente le clonage. * @version 1.10 2002-07-01 * @author Cay Horstmann */ public class CloneTest {

260

Au cur de Java 2 - Notions fondamentales

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); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } } class Employee implements Cloneable { public Employee(String n, double s) { name = n; salary = s; hireDay = new Date(); } 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 une date donne au jour dembauche (hireday) * @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) { Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime(); // Exemple de mutation de champ dinstance hireDay.setTime(newHireDay.getTime()); } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; }

Chapitre 6

Interfaces et classes internes

261

public String toString() { return "Employee[name="+name +",salary="+salary +",hireDay="+hireDay() +"]"; } private String name; private double salary; private Date hireDay; }

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 lafchage de lhorloge. Lorsque vous construisez un minuteur, vous dnissez 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 exible que transfrer une fonction car lobjet peut transporter des informations complmentaires. Bien entendu, le minuteur doit savoir quelle mthode appeler. Vous devez spcier 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 difciles utiliser, plus lents et la scurit des types ne peut pas tre vrie au moment de la compilation. Ds que vous utilisez un pointeur de fonction en C++, envisagez dutiliser une interface en Java.

262

Au cur de Java 2 - Notions fondamentales

Supposons que vous souhaitiez afcher le message "At the tone, the time is " ( la tonalit, il sera), suivi dun bip, et ce toutes les 10 secondes. Dnissez 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). 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 notications, mesur en millimes de seconde. Nous voulons tre avertis toutes les 10 secondes. Le deuxime paramtre est lobjet couteur. Enn, 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

safche, suivi dun bip. Le Listing 6.3 fait fonctionner le minuteur et son couteur. Une fois le minuteur dmarr, le programme afche un message et attend que lutilisateur clique sur OK pour sarrter. Entre-temps, lheure actuelle safche par intervalles de 10 secondes. Soyez patient lorsque vous excutez le programme. La bote de dialogue "Quit program?" (fermer le programme ?) safche 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.
Listing 6.3 : TimerTest.java
/** * @version 1.00 2000-04-13 * @author Cay Horstmann */ import java.awt.*;

Chapitre 6

Interfaces et classes internes

263

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)

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

264

Au cur de Java 2 - Notions fondamentales

Classes internes
Une classe interne est une classe qui est dnie lintrieur dune autre classe. Trois raisons justient lemploi de classes internes :
m

Les mthodes de classe internes peuvent accder aux donnes, partir de la porte o elles sont dnies, y compris les donnes qui pourraient tre des donnes prives. Les classes internes peuvent tre caches aux autres classes du mme package.

m m

Les classes internes anonymes sont utiles pour dnir 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. Enn, 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 dnit 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(); . . . }; . . . private: class Link // une classe imbrique { public: Link* next; int data; }; . . . };

Chapitre 6

Interfaces et classes internes

265

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 conit 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 :
public class TalkingClock { public TalkingClock(int interval, boolean beep) public void start() { . . . } private int interval; private boolean beep; public class TimePrinter implements ActionListener // une classe interne { . . . } }

{ . . . }

Remarquez la classe TimePrinter, qui se trouve lintrieur de la classe TalkingClock. Cela ne signie 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. Voici la classe TimePrinter plus en dtail. Sachez que la mthode actionPerformed vrie 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(); } }

266

Au cur de Java 2 - Notions fondamentales

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). Cette rfrence est invisible dans la dnition 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(); System.out.println("At the tone, the time is " + now); if (outer.beep) Toolkit.getDefaultToolkit().beep(); }

La rfrence de classe externe est dnie dans le constructeur. Le compilateur modie tous les constructeurs de classe interne, en ajoutant un paramtre pour la rfrence de classe externe. Etant donn que TimePrinter ne dnit aucun constructeur, le compilateur synthtise un constructeur par dfaut, gnrant un code comme celui-ci :
public TimePrinter(TalkingClock clock) // Code gnr automatiquement { outer = clock; }

Figure 6.3
Un objet dune classe interne possde une rfrence un objet dune classe externe.

TimePrinter outer =

TalkingClock interval = beep = 1000 true

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

Chapitre 6

Interfaces et classes internes

267

Le Listing 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.
INFO
Nous aurions pu dclarer la classe TimePrinter comme private. Dans ce cas, seules les mthodes TalkingClock auraient pu construire les objets TimePrinter. Seules les classes internes peuvent tre prives. Les classes ordinaires ont toujours une visibilit soit publique, soit sur le package.

Listing 6.4 : InnerClassTest.java


import import import import import java.awt.*; java.awt.event.*; java.util.*; javax.swing.*; javax.swing.Timer;

/** * Ce programme prsente lutilisation des classes internes. * @version 1.10 2004-02-27 * @author Cay Horstmann */ 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); } } /** * 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; }

268

Au cur de Java 2 - Notions fondamentales

/** * Lance lhorloge. */ public void start() { ActionListener listener = new TimePrinter(); Timer t = new Timer(interval, listener); t.start(); } private int interval; private boolean beep; public 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(); }

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

Chapitre 6

Interfaces et classes internes

269

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.

Utilit, ncessit et scurit des classes internes


Lorsque les classes internes ont t ajoutes au langage Java dans Java 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 dnitive, 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 chiers 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 chier de classe TalkingClock$TimePrinter.class. Pour le constater, faites cette exprience : excutez le programme ReflectionTest du Chapitre 5 et donnez-lui comme classe de rexion la classe TalkingClock$TimePrinter. Vous pouvez aussi simplement employer lutilitaire javap :
javap private nomClasse

INFO
Si vous utilisez UNIX, noubliez pas dchapper le caractre $ si vous fournissez le nom de classe sur la ligne de commande. En fait, excutez le programme ReflectionTest ou javap sous la forme :
java ReflectionTest TalkingClock\$TimePrinter

ou
javap private TalkingClock\$TimePrinter

Vous obtenez donc lafchage suivant :


public class TalkingClock$TimePrinter { public TalkingClock$TimePrinter(TalkingClock); public void actionPerformed(java.awt.event.ActionEvent); final TalkingClock this$0; }

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 TalkingClock ajout pour le constructeur.

270

Au cur de Java 2 - Notions fondamentales

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. 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 an despionner la classe TalkingClock :
class TalkingClock { public TalkingClock(int, boolean); static boolean access$0(TalkingClock); public void start(); private int interval; private boolean beep; }

Remarquez la mthode statique access$0 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)

Chapitre 6

Interfaces et classes internes

271

dans la mthode actionPerformed de la classe TimePrinter ralise, en fait, lappel suivant :


if (access$0(outer));

Y a-t-il un risque pour la scurit ? Bien sr. Il est facile un tiers dinvoquer la mthode access$0 pour lire le champ priv beep. Bien entendu, access$0 nest pas un nom autoris pour une mthode Java. Nanmoins, pour des pirates familiers de la structure des chiers de classe, il est facile de produire un chier 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 modier un chier de classe.
INFO
Les constructeurs et mthodes synthtiss peuvent tre assez complexes (passez cette info si vous navez pas le courage daborder ce sujet). Supposons que nous transformions TimePrinter en classe interne prive. Il nexiste pas de classe prive dans la machine virtuelle, le compilateur produit donc une chose formidable, une classe visible par le package avec un constructeur priv
private TalkingClock$TimePrinter(TalkingClock);

Bien entendu, personne ne peut appeler le constructeur. Il existe donc un second constructeur visible par le package :
TalkingClock$TimePrinter(TalkingClock, TalkingClock$1);

qui appelle le premier. Le compilateur traduit le constructeur dans la mthode start de la classe TalkingClock en
new TalkingClock$TimePrinter(this, null)

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 dnir les classes localement lintrieur dune seule mthode :
public void start() { 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(); } }

272

Au cur de Java 2 - Notions fondamentales

ActionListener listener = new TimePrinter(); Timer t = new Timer(interval, listener); t.start(); }

Les classes locales ne sont jamais dclares avec un spcicateur 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.

Accs aux variables nal partir de mthodes externes


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(interval, listener); t.start(); }

Notez que la classe TalkingClock na plus besoin de stocker une variable dinstance beep. Elle fait simplement rfrence la variable paramtre beep de la mthode start. 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? An de comprendre ce problme dlicat, examinons de plus prs le ux dexcution : 1. La mthode start est appele. 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 excute if (beep) . . .

Chapitre 6

Interfaces et classes internes

273

Pour que le code de la mthode actionPerformed fonctionne, la classe TimePrinter doit avoir fait une copie de la valeur de paramtre 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 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. Le compilateur dtecte laccs des variables locales, cre des champs dinstance pour chacune delles et copie les variables locales dans le constructeur an que les champs dinstance soient initialiss. Du point de vue du programmeur, laccs aux variables locales est assez plaisant. Il simplie 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 modie 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 signie la mme chose : cette variable ne peut tre affecte quune seule fois aprs sa cration. Il nest pas possible den modier ultrieurement la valeur elle est dnitive. Cela dit, il nest pas obligatoire dinitialiser une variable final lors de sa dnition. Par exemple, la variable paramtre final beep est initialise une fois aprs sa cration, lorsque la mthode start est appele (si elle est appele 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 dnie une seule fois, dans le constructeur de la classe interne. Une variable final qui nest pas initialise lors de sa dnition est souvent appele variable nale vide.

La restriction final est assez peu pratique. Supposons, par exemple, que vous souhaitiez actualiser un compteur dans la porte. Ici, nous voulons compter la frquence dappel de la mthode compareTo pendant le tri.
int counter = 0; Date[] dates = new Date[100];

274

Au cur de Java 2 - Notions fondamentales

for (int i = 0; i < dates.length; i++) dates[i] = new Date() { public int compareTo(Date other) { counter++; // ERREUR return super.compareTo(other); } }; Arrays.sort(dates); System.out.println(counter + " comparisons.");

Vous ne pouvez pas dclarer counter comme final car il est vident que vous devez lactualiser. Vous ne pouvez pas non plus le remplacer par un Integer car les objets Integer sont inaltrables. Le recours consisterait utiliser un tableau de longueur 1 :
final int[] counter = new int[1]; for (int i = 0; i < dates.length; i++) dates[i] = new Date() { public int compareTo(Date other) { counter[0]++; return super.compareTo(other); } };

La variable de tableau est toujours dclare final, mais cela signie simplement que vous ne pouvez pas lui faire rfrencer un autre tableau. Libre vous daltrer les lments du tableau. Lorsque les classes internes ont t inventes, le compilateur de prototype a automatiquement effectu cette transformation pour toutes les variables locales modies dans la classe interne. Certains programmeurs craignaient malgr tout que le compilateur produise des tas dobjets dans leur dos ; la restriction final a donc t adopte la place. Une prochaine version du langage Java pourrait dailleurs bien inverser cette dcision.

Classes internes anonymes


Lors de lutilisation de classes internes locales, vous pouvez souvent aller plus loin. Si vous ne souhaitez 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(interval, listener); t.start(); }

Chapitre 6

Interfaces et classes internes

275

Il sagit l dune syntaxe assez obscure qui signie : crer un nouvel objet dune classe qui implmente linterface ActionListener, o la mthode actionPerformed requise est celle dnie entre les accolades { }. 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.
Person queen = // un objet Person count = // un objet new Person("Mary"); Person new Person("Dracula") { ... }; dune classe interne tendant Person

Si la parenthse fermante de la liste de paramtres du constructeur est suivie dune accolade ouvrante, cela dnit une classe interne anonyme. 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. Le Listing 6.5 contient le code source complet du programme dhorloge parlante avec une classe interne anonyme. Si vous comparez ce programme celui du Listing 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.
Listing 6.5 : AnonymousInnerClassTest.java
import import import import import java.awt.*; java.awt.event.*; java.util.*; javax.swing.*; javax.swing.Timer;

/** * Ce programme prsente les classes internes anonymes. * @version 1.10 2004-02-27 * @author Cay Horstmann */

276

Au cur de Java 2 - Notions fondamentales

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 * (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 n, 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 efcace 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)

Chapitre 6

Interfaces et classes internes

277

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

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

278

Au cur de Java 2 - Notions fondamentales

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.

INFO
Les classes internes qui sont dclares dans une interface sont automatiquement statiques et publiques.

Le Listing 6.6 contient le code source complet de la classe ArrayAlg et de la classe imbrique Pair.
Listing 6.6 : StaticInnerClassTest.java
/** * Ce programme prsente lutilisation de classes internes statiques. * @version 1.01 2004-02-27 * @author Cay Horstmann */ 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 {

Chapitre 6

Interfaces et classes internes

279

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

280

Au cur de Java 2 - Notions fondamentales

Proxies
Dans cette dernire section du chapitre, nous allons tudier les proxies, une fonctionnalit devenue disponible avec la version 1.3 de Java SE. 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, les interfaces que vous devez implmenter. Cette situation se produit rarement pour les programmeurs dapplication, mais nhsitez pas passer cette section si les difcults ne vous intressent pas. Cependant, pour certaines applications systme, la souplesse que procurent les proxies peut avoir une importance capitale. Supposons que vous vouliez construire un objet dune classe qui implmente une ou plusieurs interfaces dont vous ne connaissez peut-tre pas la nature exacte au moment de la compilation. Cest l un problme ardu. Pour construire une classe relle, vous pouvez simplement utiliser la mthode newInstance ou la rexion pour trouver un constructeur. Mais vous ne pouvez pas instancier une interface. Vous devez dnir une nouvelle classe dans un programme qui sexcute. Pour rsoudre ce problme, certains programmes gnrent du code, le placent dans un chier, invoquent le compilateur puis chargent le chier 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 spciez. En particulier, elle possde les mthodes suivantes :
m m

toutes les mthodes requises par les interfaces spcies ; toutes les mthodes dnies dans la classe Object (toString, equals, etc.).

Nanmoins, vous ne pouvez pas dnir 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. Nous verrons les chargeurs de classes au Chapitre 9 du Volume II. Pour linstant, nous spcierons null pour utiliser le chargeur de classe par dfaut. Un tableau dobjets Class, un pour chaque interface implmenter. Un gestionnaire dinvocation.

m m

Deux questions restent en suspens. Comment dnissons-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 ;

Chapitre 6

Interfaces et classes internes

281

lassociation dvnements de linterface utilisateur avec des actions, dans un programme qui sexcute ; la trace des appels de mthode des ns de dbogage.

Dans notre exemple de programme, nous allons utiliser les proxies et les gestionnaires dinvocation pour tracer les appels de mthode. Nous dnissons une classe enveloppe TraceHandler qui stocke un objet envelopp (wrapped). Sa mthode invoke afche 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) { 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 une ou plusieurs interfaces Class[] interfaces = new Class[] { Comparable.class }; Object proxy = Proxy.newProxyInstance(null, interfaces, handler);

Maintenant, chaque fois quune mthode de lune des interfaces sera appele sur le proxy, le nom de la mthode et ses paramtres seront afchs, puis la mthode sera invoque sur value. Dans le programme du Listing 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. Enn, llment correspondant safche :
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] = Proxy.newInstance(. . .); // proxy pour la valeur; } // construire un entier alatoire Integer key = new Random().nextInt(elements.length) + 1;

282

Au cur de Java 2 - Notions fondamentales

// 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 dnie 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.
INFO
Comme vous lavez vu plus tt dans ce chapitre, depuis Java SE 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 afche le nom de la mthode et ses paramtres, puis invoque compareTo sur lobjet Integer envelopp. Enn, la n 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. Vous voyez que la mthode toString est mise en proxy, mme si elle nappartient pas linterface Comparable. Vous le dcouvrirez la prochaine section, certaines mthodes Object sont toujours mises en proxy.
Listing 6.7 : ProxyTest.java
import java.lang.reflect.*; import java.util.*; /** * Ce programme prsente lutilisation des proxies.

Chapitre 6

Interfaces et classes internes

283

* @version 1.00 2000-04-13 * @author Cay Horstmann */ 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; InvocationHandler handler = new TraceHandler(value); Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class } , handler); elements[i] = proxy; } // construit un entier alatoire Integer key = new Random().nextInt(elements.length) + 1; // 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]);

284

Au cur de Java 2 - Notions fondamentales

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. Toutes les classes proxy tendent la classe Proxy. Une classe proxy na quune variable dinstance le gestionnaire dinvocation qui est dni 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 du Listing 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 rednies. Les noms des classes proxy ne sont pas dnis. 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)

Dnit cette mthode pour quelle contienne laction que vous voulez excuter chaque fois quune mthode a t invoque sur lobjet proxy.

Chapitre 6

Interfaces et classes internes

285

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 prt aborder lapprentissage des techniques de programmation graphique et des interfaces utilisateur, qui commence au Chapitre 7.

7
Programmation graphique
Au sommaire de ce chapitre

Introduction Swing Cration dun cadre Positionnement dun cadre Afchage des informations dans un composant Formes 2D Couleurs Texte et polices Afchage dimages
Vous avez tudi jusqu prsent lcriture de programmes qui nacceptaient que des caractres taps au clavier, les traitaient et afchaient 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 spcier la taille et la position des fentres, dy afcher 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. Des techniques plus sophistiques de programmation graphique sont tudies au Volume II. Si, au contraire, vous souhaitez utiliser Java pour la programmation ct serveur uniquement et que la GUI ne vous intresse pas, vous pouvez aisment sauter ces chapitres.

288

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 difcile dcrire une bibliothque graphique portable de haute qualit qui dpendait des lments dune interface utilisateur native. Linterface utilisateur comme les menus, les barres de dlement et les champs de texte peuvent avoir des diffrences subtiles de comportement sur diffrentes plates-formes. Il tait donc difcile 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 requise par le systme de fentres sous-jacent 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 plate-forme 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". Swing constituait une extension de Java 1.1 et sest nalement intgr dans la bibliothque standard de Java SE 1.2. Comme la dit Duke Ellington, "Tout a nest rien si je nai pas le swing." Et Swing est maintenant le nom ofciel du kit de dveloppement dinterface graphique. 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).

Chapitre 7

Programmation graphique

289

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" 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).

Bien entendu, les lments dinterface Swing seront un peu plus lents safcher 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 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 spciques dune plate-forme. Swing procure une bonne exprience lutilisateur qui travaille sur plusieurs plates-formes.

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 spcique. Les Figures 7.1 et 7.2 montrent le mme programme en cours dexcution, lun sous Windows, lautre sous GTK (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.

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.

290

Au cur de Java 2 - Notions fondamentales

Figure 7.2
Application Swing avec le look and feel de GTK.

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".
Figure 7.3
Le look and feel "Metal" de Swing, avec le thme Ocean.

Dans Java SE 6, Sun a amlior la prise en charge du look and feel natif pour Windows et GTK. Une application Swing choisit dsormais des personnalisations des schmas de couleurs et rend dlement les boutons et barres doutils modernes. Certains prfrent que les applications Java emploient le look and feel de leurs plates-formes, dautres optent pour Metal ou pour un look and feel indpendant. Vous le verrez au Chapitre 8, il est trs facile de laisser les utilisateurs choisir leur look and feel prfr.

Chapitre 7

Programmation graphique

291

INFO
Bien que cela dpasse le cadre de cet ouvrage, sachez que le programmeur Java peut amliorer un look and feel existant ou en concevoir un qui soit entirement nouveau. Cest un travail fastidieux qui exige de spcier la mthode de dessin des divers composants Swing. 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 dcouvrir toute une srie dimplmentations look and feel intressantes. Java SE 5.0 a introduit un nouveau look and feel, appel Synth, qui facilite ce processus. Synth vous permet de dnir un nouveau look and feel en fournissant des chiers image et des descripteurs XML, sans effectuer aucune programmation.

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

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 des outils de dveloppement dinterface pour Java, mais, pour les utiliser efcacement, vous devez savoir construire linterface manuellement. Le reste du chapitre donne les bases pour afcher une fentre et dessiner son contenu.

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.

Nous allons maintenant tudier les mthodes employes le plus frquemment lorsque nous travaillons avec un composant JFrame. Le Listing 7.1 prsente un programme simple qui afche une fentre vide sur lcran, comme le montre la Figure 7.4.

292

Au cur de Java 2 - Notions fondamentales

Figure 7.4
Le plus simple des cadres visibles.

Listing 7.1 : SimpleFrameTest.java


import javax.swing.*; /** * @version 1.32 2007-06-12 * @author Cay Horstmann */ 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). Pour des raisons historiques, les classes Swing constituent une extension mais elles font partie de chaque implmentation de Java SE depuis la version 1.2. Par dfaut, un cadre a une taille assez peu utile de 0 0 pixel. Nous dnissons une sous-classe SimpleFrame dont le constructeur dnit la taille 300 200 pixels. Cest la seule diffrence entre un SimpleFrame et un JFrame. Dans la mthode main de la classe SimpleFrameTest, nous commenons par construire un objet SimpleFrame et nous lafchons. Nous devons traiter deux problmes techniques dans chaque programme Swing. Tout dabord, tous les composants Swing doivent tre congurs partir du thread de rpartition des vnements, le thread de contrle qui transfre des vnements comme des clics de souris et des

Chapitre 7

Programmation graphique

293

frappes de touche sur les composants de linterface utilisateur. Le fragment de code suivant sert excuter des instructions dans le thread de rpartition des vnements :
EventQueue.invokeLater(new Runnable() { public void run() { Instructions } });

Nous verrons les dtails au Chapitre 14. Pour lheure, considrez simplement quil sagit dune incantation magique servant lancer un programme Swing.
INFO
Vous verrez que de nombreux programmes Swing ninitialisent pas linterface utilisateur dans le thread de rpartition des vnements. Cette situation tait parfaitement acceptable pour raliser linitialisation dans le thread principal. Malheureusement, les composants Swing devenant plus complexes, les programmeurs de Sun ne pouvaient plus garantir la scurit de cette mthode. La probabilit derreur est extrmement faible et vous ne voudrez srement pas faire partie des malchanceux qui rencontrent un problme intermittent. Mieux vaut procder dans les rgles, mme si le code semble plutt mystrieux.

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. Il aurait pu tre intressant que le programme se termine aprs que le dernier cadre est devenu invisible, mais il nen est pas ainsi. Le simple fait de construire un cadre ne lafche pas automatiquement. Les cadres sont au dpart invisibles. Cela permet au programmeur dy ajouter des composants avant lafchage. Pour afcher le cadre, la mthode main appelle la mthode setVisible du cadre.
INFO
Avant Java SE 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 Java SE 1.2. Vous tes cens appeler setVisible(true) pour afcher un composant. Nanmoins, jusqu Java SE 1.4, la mthode Window.show ntait pas dprcie. En fait, elle tait assez utile, pour afcher la fentre et la faire apparatre au premier plan. Malheureusement, cet avantage a disparu avec la politique de dprciation et Java SE 5.0 dprciait la mthode show galement pour les fentres.

Aprs avoir plani les instructions dinitialisation, la mthode main se termine. Sachez quelle ne met pas n au programme, mais simplement au thread principal. Le thread de rpartition des vnements maintient le programme en activit jusqu son achvement, soit par fermeture du cadre, soit par appel la mthode System.exit.

294

Au cur de Java 2 - Notions fondamentales

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 Windows, GTK ou Mac, les oritures 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 Java SE 1.4, vous pouvez dsactiver toutes les dcorations de cadre en appelant frame.setUndecorated(true).

Positionnement dun cadre


La classe JFrame ne fournit que peu de mthodes capables de modier 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 m

Les mthodes setLocation et setBounds dnissent la position du cadre. La mthode setIconImage indique au systme de fentres licne afcher dans la barre de titre, la fentre de commutation des tches, etc. La mthode setTitle spcie le texte afch dans la barre de titre. La mthode setResizable reoit un paramtre boolen pour dterminer si la taille dun cadre peut tre modie par lutilisateur.

m m

La Figure 7.5 illustre la chane dhritage de la classe JFrame.


ASTUCE
Les notes API de cette section proposent les mthodes les plus importantes, selon nous, pour donner un aspect correct aux cadres. Certaines sont dnies dans la classe JFrame. Dautres proviennent des diverses superclasses de JFrame. A un certain point, vous devrez peut-tre faire des recherches dans la documentation API pour retrouver des mthodes usage spcique. Malheureusement, cela est un peu fastidieux avec les mthodes hrites. Ainsi, par exemple, la mthode toFront sapplique aux objets du type JFrame, mais puisquelle est simplement hrite de la classe Window, la documentation de JFrame ne lexplique pas. Sil vous semble quune mthode serait ncessaire et quelle ne soit pas explique dans la documentation de la classe pour laquelle vous travaillez, essayez de parcourir la documentation API des mthodes des superclasses de cette classe. Le haut de chaque page API possde des liens hypertexte vers les superclasses et les mthodes hrites sont rpertories sous le rsum des mthodes nouvelles et remplaces.

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

Chapitre 7

Programmation graphique

295

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 modier 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 dans AWT et Swing.
Objet

Composant

Conteneur

JComponent

Fentre

JPanel

Cadre

JFrame

Vous pouvez aussi donner le contrle du placement au systme de fentres. Si vous appelez
setLocationByPlatform(true);

avant dafcher le cadre, le systme de fentres choisit lemplacement (mais non la taille), gnralement avec un lger dcalage par rapport la dernire fentre.

296

Au cur de Java 2 - Notions fondamentales

INFO
Pour un cadre, les coordonnes de setLocation et setBounds sont relatives lcran. Pour dautres composants, placs dans un conteneur, les coordonnes sont relatives au conteneur, comme vous le verrez au Chapitre 9.

Proprits des cadres


De nombreuses mthodes de classes de composants sont fournies par les paires get/set, notamment les mthodes de la classe Frame :
public String getTitle() public void setTitle(String title)

Cette paire get/set est appele une proprit, elle possde un nom et un type. Le nom sobtient en modiant la premire lettre venant aprs get ou set. Par exemple, la classe Frame possde une proprit de nom title et de type String. Conceptuellement, title est une proprit du cadre. Lorsque nous la dnissons, nous nous attendons ce que le titre change lcran. Lorsque nous obtenons la proprit, nous nous attendons recevoir la valeur dnie. Peu importe comment la classe Frame implmente cette proprit. Utilise-t-elle simplement un cadre pair pour stocker le titre ? Ou dispose-t-elle dun champ dinstance
private String title; // non requis pour la proprit

Si la classe ne possde pas de champ dinstance correspondant, nous ne savons pas comment sont implmentes les mthodes get et set (et cela nous est gal). Elles se contentent peut-tre de lire et dcrire le champ dinstance ? Ou font-elles plus, par exemple en avertissant le systme de fentres que le titre a chang. Il existe toutefois une exception la convention get/set : pour les proprits de type boolean, la mthode get dmarre par is. Ainsi, par exemple, les deux mthodes suivantes dnissent la proprit locationByPlatform :
public boolean isLocationByPlatform() public void setLocationByPlatform(boolean b)

Nous verrons les proprits plus en dtail au Chapitre 8 du Volume II.


INFO
De nombreux langages de programmation, notamment Visual Basic et C#, disposent dune prise en charge intgre pour les proprits. Il est possible quune prochaine version de Java possde galement une construction de langage allant dans ce sens.

Dterminer une taille de cadre adquate


Noubliez pas que si vous ne spciez pas explicitement la taille dun cadre, celui-ci aura par dfaut une largeur et une hauteur de 0 pixel. Pour simplier notre programme, nous avons donn au cadre une taille qui devrait tre accepte par la plupart des systmes dafchage. Cependant, dans une

Chapitre 7

Programmation graphique

297

application professionnelle, vous devez dabord dterminer la rsolution de lcran an 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. 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 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;

Nous utilisons 50 % de ces valeurs pour la taille du cadre et indiquons au systme de fentrage de placer le cadre :
setSize(screenWidth / 2, screenHeight / 2); setLocationByPlatform(true);

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 safche 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. Le Listing 7.2 prsente le programme complet. Lorsque vous excuterez le programme, remarquez licne "Core Java". Voici quelques conseils supplmentaires pour grer les cadres :
m

Si votre cadre ne contient que des composants standard comme des boutons et des champs de texte, il vous suft dappeler la mthode pack pour rgler sa taille. Le cadre sera rgl sur la plus petite taille qui permet de loger tous les composants. Il est assez frquent de dnir le cadre principal dun programme sur la taille maximale. Depuis Java SE 1.4, vous pouvez maximiser un cadre en appelant :
frame.setExtendedState(Frame.MAXIMIZED_BOTH);

Vous pouvez aussi retenir la manire dont lutilisateur place et dimensionne le cadre de votre application et restaurer ces limites lorsque vous relancez lapplication. Vous verrez au Chapitre 10 comment utiliser lAPI Preferences dans ce but. Si vous crivez une application qui prote de lafchage multiple, utilisez les classes GraphicsEnvironment et GraphicsDevice pour obtenir les dimensions des crans. La classe GraphicsDevice vous permet aussi dexcuter votre application en mode plein cran.

298

Au cur de Java 2 - Notions fondamentales

Listing 7.2 : SizedFrameTest.java


import java.awt.*; import javax.swing.*; /** * @version 1.32 2007-04-14 * @author Cay Horstmann */ public class SizedFrameTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { SizedFrame frame = new SizedFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } } class SizedFrame extends JFrame { public SizedFrame() { // extraire les dimensions de lcran Toolkit kit = Toolkit.getDefaultToolkit(); Dimension screenSize = kit.getScreenSize(); int screenHeight = screenSize.height; int screenWidth = screenSize.width; // dfinir la largeur et la hauteur du cadre et // laisser la plate-forme choisir lemplacement lcran setSize(screenWidth / 2, screenHeight / 2); setLocationByPlatform(true); // dfinir licne et le titre du cadre Image img = kit.getImage("icon.gif"); setIconImage(img); setTitle("SizedFrame"); } } java.awt.Component 1.0

boolean isVisible() void setVisible(boolean b)

Rcuprent ou dnissent la proprit visible. A lorigine, les composants sont visibles, lexception des composants de haut niveau, comme JFrame.

Chapitre 7

Programmation graphique

299

void setSize(int width, int height) 1.1

Redimensionne le composant sur la largeur et la hauteur spcies.


void setLocation(int x, int y) 1.1

Dplace le composant vers un nouvel emplacement. Les coordonnes x et y utilisent les coordonnes du conteneur si le composant nest pas de haut niveau ou les coordonnes de lcran si le composant est de haut niveau (par exemple, a JFrame).

void setBounds(int x, int y, int width, int height) 1.1

Dplace et redimensionne le composant.


Dimension getSize() 1.1 void setSize(Dimension d) 1.1

Rcuprent ou dnissent la proprit size de ce composant.


java.awt.Window 1.0

void toFront()

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

boolean isLocationByPlatform() 5.0 void setLocationByPlatform(boolean b) 5.0

Rcuprent ou dnissent la proprit locationByPlatform. Lorsque la proprit est dnie avant que cette fentre ne soit afche, la plate-forme choisit un emplacement adquat.
java.awt.Frame 1.0

boolean isResizable() void setResizable(boolean b)

Rcuprent ou dnissent la proprit resizable. Lorsque la proprit est dnie, lutilisateur peut modier la taille du cadre.

String getTitle() void setTitle(String s)

Rcuprent ou dnissent la proprit title qui dtermine le texte de la barre de titre du cadre.
Image getIconImage() void setIconImage(Image image)

Rcuprent ou dnissent la proprit iconImage qui dtermine licne du cadre. Le systme de fentrage peut afcher licne dans le cadre de la dcoration ou dautres endroits.

boolean isUndecorated() 1.4 void setUndecorated(boolean b) 1.4

Rcuprent ou dnissent la proprit undecorated. Lorsque la proprit est dnie, le cadre est afch sans dcorations, comme une barre de titre ou un bouton de fermeture. Cette mthode doit tre appele avant que le cadre soit afch.

int getExtendedState() 1.4

300

Au cur de Java 2 - Notions fondamentales

void setExtendedState(int state) 1.4

Rcuprent ou dnissent ltat tendu 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()

Rcupre la taille de lcran.


Image getImage(String filename)

Charge une image partir du chier spci par filename.

Afchage des informations dans un composant


Nous allons voir maintenant comment afcher des informations lintrieur dun cadre. Par exemple, au lieu dafcher "Not a Hello, World program" en mode texte dans une fentre de console, comme nous lavons fait au Chapitre 3, nous afcherons le message dans un cadre (voir Figure 7.6).
Figure 7.6
Un cadre qui afche des informations.

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 autre composant 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);

Chapitre 7

Programmation graphique

301

Jusqu Java SE 1.4, la mthode add de la classe JFrame tait dnie de manire dclencher une exception avec le message "Do not use JFrame.add(). Use JFrame.getContentPane().add() instead". Depuis Java SE 5.0, la mthode JFrame.add nessaie plus de rduquer les programmeurs et appelle simplement add sur le volet de contenu. Ainsi, depuis Java SE 5.0, vous pouvez simplement utiliser lappel :
frame.add(c);

Figure 7.7
La structure interne dun cadre JFrame.

Titre Cadre Volet racine Volet en couche Barre de menu (en option) Volet de contenu Vitrine

Dans notre cas, nous dsirons uniquement ajouter au contenu un composant sur lequel nous crirons le message. Pour dessiner sur un composant, vous dnissez une classe qui tend JComponent et remplace la mthode paintComponent dans cette classe. La mthode paintComponent 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 dnie 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 dafchage de Windows ou un contexte graphique en programmation X11.

302

Au cur de Java 2 - Notions fondamentales

Voici comment crer un panneau, sur lequel vous pourrez dessiner :


class MyComponent extends JComponent { 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 notication 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 modi 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 au-dessus dune fentre existante, cette dernire doit tre redessine, car son afchage 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 afchs.
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. Lafchage 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 signie 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 NotHelloWorldComponent extends JComponent { public void paintComponent(Graphics g) {

Chapitre 7

Programmation graphique

303

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

Le Listing 7.3 montre le code complet.


INFO
Au lieu dtendre JComponent, certains programmeurs prfrent tendre la classe JPanel. Un JPanel a pour but dtre un conteneur dautres composants, mais il est aussi possible de dessiner dessus. Il y a une seule diffrence. Un panneau est opaque, ce qui signie quil est charg de dessiner tous les pixels dans les limites. La manire la plus simple dy parvenir consiste remplir le panneau avec la couleur darrire-plan, en appelant super.paintComponent dans la mthode paintComponent de chaque sous-classe de panneau :
class NotHelloWorldPanel extends JPanel { public void paintComponent(Graphics g) { super.paintComponent(g); . . . // code de dessin } }

Listing 7.3 : NotHelloWorld.java


import javax.swing.*; import java.awt.*; /** * @version 1.32 2007-06-12 * @author Cay Horstmann */ public class NotHelloWorld { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { NotHelloWorldFrame frame = new NotHelloWorldFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } } /** * Un cadre qui contient un panneau de message */

304

Au cur de Java 2 - Notions fondamentales

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; } /** * Un panneau qui affiche un message. */ class NotHelloWorldPanel extends JPanel { public void paintComponent(Graphics 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.


Component add(Component c)

Ajoute et renvoie le composant donn au volet de contenu de ce cadre (avant Java SE 5.0, cette mthode dclenchait une exception).
java.awt.Component 1.0

void repaint()

Provoque un nouveau dessin du composant "ds que possible".


public void repaint(int x, int y, int width, int height)

Provoque un nouveau dessin dune partie du composant "ds que possible".


javax.swing.JComponent 1.2

void paintComponent(Graphics g)

Surcharge cette mthode pour dcrire la manire dont votre composant doit tre dessin.

Chapitre 7

Programmation graphique

305

Formes 2D
Depuis la version 1.0 de Java, 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. Java SE 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 II 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. Depuis Java SE 2, les mthodes telles que paintComponent reoivent automatiquement un objet de la classe Graphics2D. Il suft 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 II 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 ottante. Cela est souvent pratique, car vous pouvez spcier

306

Au cur de Java 2 - Notions fondamentales

pour vos formes des coordonnes qui sont signicatives 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 ottante. La simple prcision est sufsante aprs tout, le but ultime des calculs gomtriques est de dnir 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 ottante 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 inexible 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

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 sufxe F la constante virgule ottante :
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 sufxes 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 Rectangle2D.
Rectangle2D

Rectangle2D .Float

Rectangle2D .Double

Chapitre 7

Programmation graphique

307

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); 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 forme. 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);

308

Au cur de Java 2 - Notions fondamentales

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.

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 dnit plus de vingt 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 enn deux classes hrites de Java 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 spcier :
m m

les coordonnes x et y du coin suprieur gauche ; la largeur et la hauteur.


Ellipse2D e = new Ellipse2D.Double(150, 200, 100, 50);

Pour les ellipses, ces valeurs font rfrence au rectangle englobant. Par exemple, 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

Chapitre 7

Programmation graphique

309

Figure 7.10
Relations entre les classes Shape.
Point2D Forme

Point

Line2D

Forme rectangulaire

Ellipse2D

Rectangle2D

Rectangle

Si p nest pas le coin suprieur gauche, lune des diffrences de coordonnes, ou les deux, sera ngative 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 du Listing 7.4 trace un rectangle, lellipse incluse dans le rectangle, une diagonale du rectangle et un cercle ayant le mme centre que le rectangle. La Figure 7.11 montre le rsultat.

310

Au cur de Java 2 - Notions fondamentales

Figure 7.11
Rectangles et ellipses.

Listing 7.4 : DrawTest.java


import java.awt.*; import java.awt.geom.*; import javax.swing.*; /** * @version 1.32 2007-04-14 * @author Cay Horstmann */ public class DrawTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { 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);

Chapitre 7

Programmation graphique

311

// ajouter un panneau au cadre DrawComponent component = new DrawComponent(); add(component); } public static final int DEFAULT_WIDTH = 400; public static final int DEFAULT_HEIGHT = 400; } /** Un composant qui affiche des rectangles et des ellipses. */ class DrawComponent extends JComponent { public void paintComponent(Graphics 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()

312

Au cur de Java 2 - Notions fondamentales

double getMaxX() double getMaxY()

Renvoient le centre, les valeurs x ou y minimum ou maximum du rectangle englobant.


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. Par exemple :
g2.setPaint(Color.red); g2.drawString("Warning!", 100, 100);

Vous pouvez remplir de couleur lintrieur de formes fermes (rectangles ou ellipses, par exemple). Appelez simplement fill au lieu de draw :
Rectangle2D rect = . . .; g2.setPaint(Color.RED); g2.fill(rect); // remplir rect de rouge

Chapitre 7

Programmation graphique

313

Pour dessiner avec plusieurs couleurs, utilisez draw ou fill, puis choisissez une autre couleur et rptez lopration. Les couleurs sont dnies laide de la classe Color. La classe java.awt.Color propose des constantes prdnies pour les treize couleurs standard :
BLACK, BLUE, CYAN, DARK_GRAY, GRAY, GREEN, LIGHT_GRAY, MAGENTA, ORANGE, PINK, RED, WHITE, YELLOW

INFO
Avant Java SE 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. Vous pouvez maintenant crire les noms des couleurs standard en majuscules ou, pour une compatibilit avec les versions antrieures, en minuscules.

Vous pouvez spcier 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 dnition 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 dnitions de "peinture" plus complexes, comme des images ou des teintes nuances. Consultez le chapitre du Volume II 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 dnir les couleurs.

Pour spcier la couleur darrire-plan (ou de fond), utilisez la mthode setBackground de la classe Component, qui est un anctre de JComponent :
MyComponent p = new MyComponent(); p.setBackground(Color.PINK);

Il existe aussi une mthode setForeground. Elle spcie 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().

314

Au cur de Java 2 - Notions fondamentales

Java fournit des noms prdnis 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)

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.1 dcrit les couleurs systme.
Tableau 7.1 : Couleurs du systme

Nom desktop activeCaption activeCaptionText activeCaptionBorder inactiveCaption inactiveCaptionText inactiveCaptionBorder window windowBorder windowText menu menuText text textText textInactiveText textHighlight textHighlightText control controlText controlLtHighlight controlHighlight controlShadow controlDkShadow scrollbar info infoText

Objet Arrire-plan du bureau Arrire-plan des titres Texte des titres Bordure des titres Arrire-plan des titres inactifs Texte des titres inactifs Bordure des titres inactifs Arrire-plan des fentres Bordure des fentres Texte lintrieur dune fentre Arrire-plan des menus Texte des menus Arrire-plan du texte Couleur du texte Texte des contrles inactifs Arrire-plan du texte en surbrillance Couleur du texte en surbrillance Arrire-plan des contrles Texte des contrles Couleur de surbrillance claire des contrles Couleur de surbrillance des contrles Ombre des contrles Ombre fonce des contrles Arrire-plan des barres de dlement Arrire-plan du texte des bulles daide Couleur du texte des bulles daide

Chapitre 7

Programmation graphique

315

java.awt.Color 1.0

Color(int r, int g, int b)

Cre un objet couleur. Paramtres : r g b


java.awt.Graphics 1.0

Valeur de la composante rouge (0-255). Valeur de la composante verte (0-255). Valeur de la composante bleue (0-255).

Color getColor() void setColor(Color c)

Rcuprent ou dnissent la couleur courante. Toutes les oprations graphiques ultrieures utiliseront la nouvelle couleur. Paramtres :

Nouvelle couleur.

java.awt.Graphics2D 1.2 Paint getPaint() void setPaint(Paint p)

Rcuprent ou dnissent les paramtres de peinture de ce contexte graphique. La classe Color implmente linterface Paint. Vous pouvez donc utiliser cette mthode pour affecter les attributs une couleur.

void fill(Shape s)

Remplit la forme de la peinture actuelle.


java.awt.Component 1.0

Color getBackground() void setBackground (Color c)

Rcuprent ou dnissent la couleur darrire-plan. Paramtres :


Nouvelle couleur darrire-plan.

Color getForeground() void setForeground(Color c)

Rcuprent ou dnissent la couleur davant-plan. Paramtres : c Nouvelle couleur davant-plan.

Texte et polices
Le programme NotHelloWorld au dbut de ce chapitre afchait une chane avec la fonte par dfaut. Il est possible dcrire un texte avec une fonte diffrente. Une fonte est spcie grce son nom de police. Un nom de police se compose dun nom de famille de polices, comme "Helvetica" et dun sufxe 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

316

Au cur de Java 2 - Notions fondamentales

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 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 quelque soixante-dix fontes supplmentaires. 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 dnit 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. En outre, le JDK de Sun comprend toujours trois familles de polices nommes "Lucida Sans", "Lucida Bright" et "Lucida Sans Typewriter". Pour crire des caractres dans une fonte, vous devez dabord crer un objet de la classe Font. Vous spciez le nom de fonte, son style et la taille en points. Voici un exemple de construction dun objet Font :
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14);

Chapitre 7

Programmation graphique

317

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

INFO
La correspondance entre les noms de fontes logiques et physiques est dnie dans le chier fontcong.properties du sous-rpertoire jre/lib de linstallation Java. Pour plus dinformations sur ce chier, voir http://java.sun.com/javase/ 6/docs/technotes/guides/intl/fontcong.html.

Vous pouvez lire les fontes TrueType ou PostScript Type 1. Un ux dentre est ncessaire pour la fonte gnralement partir dun chier disque ou dune adresse URL (voir le Chapitre 1 du Volume II pour plus dinformations sur les ux). 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 = f1.deriveFont(14.0F);

ATTENTION
Il existe deux versions surcharges de la mthode deriveFont. Lune delles (ayant un paramtre float) dnit le corps de la police, lautre (avec un paramtre int), son style. Ds lors, f1.deriveFont(14) dnit le style et non le corps (le rsultat est une police italique car la reprsentation binaire de 14 dnit le type ITALIC mais non le type BOLD).

Les fontes Java contiennent les habituels caractres ASCII et des symboles. Par exemple, si vous afchez le caractre \u2297 de la fonte Dialog, vous obtenez un caractre reprsentant une croix dans un cercle. Seuls sont disponibles les symboles dnis dans le jeu de caractres Unicode. Voici maintenant le code afchant 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);

318

Au cur de Java 2 - Notions fondamentales

Nous allons maintenant centrer la chane dans son composant 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 m 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.12). 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.12
Illustration des termes de typographie.
ligne de base hauteur ligne de base

e b k p g

jambage ascendant jambage descendant interligne

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

Chapitre 7

Programmation graphique

319

Le code suivant utilise toutes ces informations pour centrer une chane lintrieur de son composant 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 composant. 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.
INFO
Pour calculer des dimensions de mise en page en dehors de la mthode paintComponent, sachez que le contexte de rendu de la fonte ne peut pas tre obtenu de lobjet Graphics2D. Appelez plutt la mthode getFontMetrics de la classe JComponent, puis appelez getFontRenderContext.
FontRenderContext context = getFontMetrics(f).getFontRenderContext();

Pour montrer que le positionnement est prcis, lexemple dessine la ligne de base et le rectangle englobant. La Figure 7.13 montre lafchage lcran, le Listing 7.5 le listing du programme.
Figure 7.13
Dessin de la ligne de base et des limites de la chane.

Listing 7.5 : FontTest.java


import import import import java.awt.*; java.awt.font.*; java.awt.geom.*; javax.swing.*;

/** * @version 1.33 2007-04-14 * @author Cay Horstmann */ public class FontTest

320

Au cur de Java 2 - Notions fondamentales

{ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { FontFrame frame = new FontFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } } /** * Un cadre avec un composant pour un message texte */ class FontFrame extends JFrame { public FontFrame() { setTitle("FontTest"); setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); // ajouter un composant au cadre FontComponent component = new FontComponent(); add(component); } public static final int DEFAULT_WIDTH = 300; public static final int DEFAULT_HEIGHT = 200; } /** * Un composant affichant un message centr dans une bote. */ class FontComponent extends JComponent { public void paintComponent(Graphics 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 double x = (getWidth() - bounds.getWidth()) / 2; double y = (getHeight() - bounds.getHeight()) / 2;

Chapitre 7

Programmation graphique

321

// 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.LIGHT_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"). Le style (Font.PLAIN, Font.BOLD, Font.ITALIC ou Font.BOLD +Font.ITALIC). La taille en points (par exemple, 12).

style size

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.

LineMetrics getLineMetrics(String s, FontRenderContext context) 1.2

Renvoie un objet Line pour dterminer ltendue de la chane.

322

Au cur de Java 2 - Notions fondamentales

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

Font getFont() void setFont(Font font)

Renvoient ou dnissent la fonte actuelle. Cette fonte sera employe dans les oprations ultrieures dafchage 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 x y


java.awt.Graphics2D 1.2

La chane dessiner. Coordonne x du dbut de la chane. Coordonne y de la ligne de base de la chane.

FontRenderContext getFontRenderContext ()

Extrait un contexte de rendu de fonte qui spcie 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 x y La chane dessiner. Coordonne x du dbut de la chane. Coordonne y de la ligne de base de la chane.

Chapitre 7

Programmation graphique

323

javax.swing.JComponent 1.2

FontMetrics getFontMetrics(Font f) 5.0

Renvoie la mesure de la fonte donne. La classe FontMetrics est un prcurseur de la classe LineMetrics.
java.awt.FontMetrics 1.0

FontRenderContext getFontRenderContext() 1.2

Renvoie un contexte de rendu de fonte pour la fonte donne.

Afchage dimages
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 Volume II, 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 chiers locaux ou sur Internet, vous pouvez les lire dans une application Java et les afcher sur des objets de type Graphics. Depuis Java SE 1.4, la lecture dune image est trs simple. Si limage est stocke dans un chier 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 afche un stack trace le cas chant. La variable image contient alors une rfrence un objet qui encapsule les donnes image. Vous pouvez afcher limage grce la mthode drawImage de la classe Graphics :
public void paintComponent(Graphics g) { . . . g.drawImage(image, x, y, null); }

Le Listing 7.6 va un peu plus loin et afche limage en mosaque dans la fentre. Le rsultat ressemble celui de la Figure 7.14. Lafchage 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);

324

Au cur de Java 2 - Notions fondamentales

Figure 7.14
Afchage dune image en mosaque dans une fentre.

Le Listing 7.6 prsente le code source du programme de dmonstration permettant dafcher une image.
Listing 7.6 : ImageTest.java
import import import import java.awt.*; java.io.*; javax.imageio.*; javax.swing.*;

/** * @version 1.33 2007-04-14 * @author Cay Horstmann */ public class ImageTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { ImageFrame frame = new ImageFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } } /** * Un cadre avec un composant pour une image */ class ImageFrame extends JFrame { public ImageFrame() { setTitle("ImageTest"); setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); // ajouter un composant au cadre ImageComponent component = new ImageComponent(); add(component); }

Chapitre 7

Programmation graphique

325

public static final int DEFAULT_WIDTH = 300; public static final int DEFAULT_HEIGHT = 200; } /** * Un composant qui affiche une image en mosaque */ class ImageComponent extends JComponent { public ImageComponent() { // acqurir limage try { image = ImageIO.read(new File("blue-ball.gif")); } catch (IOException e) { e.printStackTrace(); } } public void paintComponent(Graphics 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 composant 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.imageio.ImageIO 1.4

static BufferedImage read(File f) static BufferedImage read(URL u)

Renvoient une image partir du chier donn ou de lURL.


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 ne soit compltement dessine.

326

Au cur de Java 2 - Notions fondamentales

Paramtres :

img x y observer

Limage dessiner. Coordonne x du coin suprieur gauche. Coordonne y du coin suprieur gauche. Lobjet qui doit tre noti en ce qui concerne la progression de lafchage (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 an quelle occupe une zone ayant la largeur et la hauteur spcies. Remarque : cette mthode peut renvoyer un rsultat avant que limage ne soit compltement dessine. Paramtres : img x y width height observer

Limage dessiner. Coordonne x du coin suprieur gauche. Coordonne y du coin suprieur gauche. Largeur souhaite pour limage. Hauteur souhaite pour limage. Lobjet qui doit tre noti en ce qui concerne la progression de lafchage (cette valeur peut tre null).

void copyArea(int x, int y, int width, int height, int dx, int dy)

Copie une zone de lcran. Paramtres : x y width height dx dy Coordonne x du coin suprieur gauche de la zone source. Coordonne y du coin suprieur gauche de la zone source. Largeur de la zone source. Hauteur de la zone source. Distance horizontale entre la zone source et la zone cible. Distance verticale entre la zone source et la zone cible.

Cela conclut notre introduction sur la programmation du graphisme dans Java. Pour passer des techniques plus avances, dcouvrez le graphisme et les images 2D dans le Volume II. Au prochain chapitre, nous verrons la raction dun programme une saisie utilisateur.

8
Gestion des vnements
Au sommaire de ce chapitre

Introduction la gestion des vnements Actions Evnements de la souris Hirarchie des vnements AWT
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 des composants de linterface utilisateur et des dispositifs dentre. Nous verrons galement comment utiliser les actions, une technique plus structure pour traiter les vnements daction.

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 xe dvnements, et il est impossible de modier 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 vrie constamment la le dvnements (en gnral, ce code est imbriqu dans une boucle gante contenant une norme instruction switch !). Cette technique

328

Au cur de Java 2 - Notions fondamentales

est videmment peu lgante et difcile 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 le 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 dlement) 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 exibilit bien suprieure celle de Visual Basic, o lcouteur est prdtermin. 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 notication 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. 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.

La Figure 8.1 montre les relations entre les classes de gestion dvnement et les interfaces.
Figure 8.1
Relation entre des sources dvnements et des couteurs.
Source dvnement 1 * <<jeu de un ou plus>> Ecouteur dvnement

implmente

Interface dcouteur

Chapitre 8

Gestion des vnements

329

Voici un exemple permettant de spcier un couteur :


ActionListener listener = . . .; JButton button = new JButton("Ok"); button.addActionListener(listener);

Lobjet listener recevra dsormais une notication 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. 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. Une source dvnement, par exemple un bouton, peut avoir plusieurs couteurs. Dans ce cas, le bouton appelle les mthodes actionPerformed de tous les couteurs, chaque fois que lutilisateur clique sur le bouton. La Figure 8.2 montre linteraction entre la source de lvnement, lcouteur dvnement et lobjet vnement.
Figure 8.2
Notication dvnement.
MyFrame

new

JButton

new addActionListener

MyListener

actionPerformed

330

Au cur de Java 2 - Notions fondamentales

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. Pour cet exemple, nous prsenterons un panneau rempli de trois boutons. Trois objets couteurs sont ajouts comme couteurs daction des boutons. 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 spcions 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 :
JButton yellowButton = new JButton("Yellow"); JButton blueButton = new JButton("Blue"); JButton redButton = new JButton("Red"); buttonPanel.add(yellowButton); buttonPanel.add(blueButton); buttonPanel.add(redButton);

Le rsultat est montr la Figure 8.3.

Figure 8.3
Un panneau contenant des boutons.

Il faut maintenant crire le code qui permet 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)

Chapitre 8

Gestion des vnements

331

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 modier 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 dnissons 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 dni Color.YELLOW et la mthode peut alors poursuivre la dnition de la couleur darrire-plan du panneau. Il reste un problme. Lobjet ColorAction na pas accs la variable buttonPanel. Vous pouvez rsoudre ce problme de deux faons. Vous pouvez stocker le panneau dans lobjet ColorAction et le dnir 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

332

Au cur de Java 2 - Notions fondamentales

accder au 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 ButtonFrame :
class ButtonPanel extends JPanel { . . . private class ColorAction implements ActionListener { . . . public void actionPerformed(ActionEvent event) { buttonPanel.setBackground(backgroundColor); } private Color backgroundColor; } private JPanel buttonPanel; }

Examinez attentivement la mthode actionPerformed. La classe ColorAction na pas de champ buttonPanel. Mais la classe externe ButtonFrame en possde. 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 modi par lcouteur. Le Listing 8.1 contient la totalit du programme. Chaque fois que vous cliquez sur lun des boutons, lcouteur daction appropri modie la couleur darrire-plan du panneau.
Listing 8.1 : ButtonTest.java
import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * @version 1.33 2007-06-12 * @author Cay Horstmann */ public class ButtonTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { ButtonFrame frame = new ButtonFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } }

Chapitre 8

Gestion des vnements

333

/** * Un cadre avec un panneau contenant des boutons */ class ButtonFrame extends JFrame { public ButtonFrame() { setTitle("ButtonTest"); setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); // crer les boutons JButton yellowButton = new JButton("Yellow"); JButton blueButton = new JButton("Blue"); JButton redButton = new JButton("Red"); buttonPanel = new JPanel(); // ajouter les boutons au panneau buttonPanel.add(yellowButton); buttonPanel.add(blueButton); buttonPanel.add(redButton); // ajouter le panneau au cadre add(buttonPanel); // 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) { backgroundColor = c; } public void actionPerformed(ActionEvent event) { buttonPanel.setBackground(backgroundColor); } private Color backgroundColor; } private JPanel buttonPanel; public static final int DEFAULT_WIDTH = 300; public static final int DEFAULT_HEIGHT = 200; }

334

Au cur de Java 2 - Notions fondamentales

javax.swing.JButton 1.2

JButton(String label) JButton(Icon icon) JButton(String label, Icon icon)

Construisent un bouton. La chane du label peut tre du texte pur ou HTML partir de Java SE 1.3 ; par exemple, "<html><b>Ok</b></html>".
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 chier.

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. Voici un bon exemple de la faon dont les classes internes anonymes peuvent rellement simplier votre code. Si vous examinez le code du Listing 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 simplier ces tches :
public void makeButton(String name, Color backgroundColor) { JButton button = new JButton(name); buttonPanel.add(button); ColorAction action = new ColorAction(backgroundColor); button.addActionListener(action); }

Nous appelons alors simplement :


makeButton("yellow", Color.YELLOW); makeButton("blue", Color.BLUE); makeButton("red", Color.RED);

Chapitre 8

Gestion des vnements

335

Vous pouvez encore simplier. Notez que la classe ColorAction nest ncessaire quune fois, dans la mthode makeButton. Vous pouvez donc en faire une classe anonyme :
public void makeButton(String name, final Color backgroundColor) { JButton button = new JButton(name); buttonPanel.add(button); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { buttonPanel.setBackground(backgroundColor); } }); }

Le code de lcouteur daction a t simpli. 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.
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) { buttonPanel.setBackground(backgroundColor); } });

Cest--dire que laction du bouton consiste dnir 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.

INFO
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 amnent le composant des sources dvnement implmenter linterface ActionListener. Puis le conteneur se dnit lui-mme comme couteur, comme ceci :
yellowButton.addActionListener(this); blueButton.addActionListener(this); redButton.addActionListener(this);

336

Au cur de Java 2 - Notions fondamentales

Notez que, maintenant, les trois boutons nont plus dcouteurs individuels. Ils partagent un unique objet couteur, le cadre avec les boutons. Par consquent, la mthode actionPerformed doit dterminer sur quel bouton il a t cliqu.
class ButtonFrame extends JFrame implements ActionListener { . . . public void actionPerformed(ActionEvent event) { Object source = event.getSource(); if (source == yellowButton) . . . else if (source == blueButton) . . . else if (source == redButton ) . . . else . . . } }

Vous le voyez, cela peut tre un peu confus et nous le dconseillons.

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) static Object create(Class listenerInterface, Object target, String action, String eventProperty, String listenerMethod)

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 dnitif devient le paramtre de laction. Par exemple, la proprit "source.text" est transforme en appels aux mthodes getSource et getText.

Chapitre 8

Gestion des vnements

337

Crer des couteurs contenant un seul appel de mthode


Java SE 1.4 introduit un mcanisme qui vous permet de spcier des couteurs dvnement simples sans programmer de classes internes. Supposons par exemple que vous ayez un bouton intitul Load dont le gestionnaire dvnement contienne 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( EventHandler.create(ActionListener.class, frame, "loadData"));

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

Les noms des proprits source et text se transforment en appels de mthode getSource et getText.

Exemple : modication 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 chier 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 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 Macintosh sont uniquement fournis avec les versions Windows ou Macintosh de lenvironnement dexcution de Java.

338

Au cur de Java 2 - Notions fondamentales

ASTUCE
Comme les lignes dbutant par le caractre # sont ignores dans les chiers de proprits, vous pouvez spcier plusieurs look and feel dans le chier 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 modier le look and feel de cette manire. Un programme Swing ne lit quune seule fois le chier swing.properties, au dmarrage.

La deuxime mthode consiste modier 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();

Le Listing 8.2 est un programme de dmonstration qui montre comment changer de look and feel (voir Figure 8.4). Ce programme ressemble beaucoup celui du Listing 8.1. Conformment aux explications de la prcdente section, nous employons une mthode assistante makeButton et une classe interne anonyme pour spcier 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 externe PlafFrame la mthode updateComponentTreeUI. Souvenez-vous, nous avons vu au Chapitre 6 que le pointeur this de lobjet extrieur doit avoir pour prxe le nom de la classe externe :
SwingUtilities.updateComponentTreeUI( PlafPanel.this);

Chapitre 8

Gestion des vnements

339

Figure 8.4
Modication du look and feel.

Listing 8.2 : PlafTest.java


import java.awt.EventQueue; import java.awt.event.*; import javax.swing.*; /** * @version 1.32 2007-06-12 * @author Cay Horstmann */ public class PlafTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { 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 { public PlafFrame() { setTitle("PlafTest"); setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); buttonPanel = new JPanel();

340

Au cur de Java 2 - Notions fondamentales

UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels(); for (UIManager.LookAndFeelInfo info: infos) makeButton(info.getName(), info.getClassName()); add(buttonPanel); } /** * 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); ButtonPanel.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); SwingUtilities.updateComponentTreeUI(PlafFrame.this); } catch(Exception e) { e.printStackTrace(); } } }); } private JPanel buttonPanel; public static final int DEFAULT_WIDTH = 300; public static final int DEFAULT_HEIGHT = 200; } javax.swing.UIManager 1.2

static UIManager.LookAndFeelInfo[] getInstalledLookAndFeels()

Rcupre un tableau dobjets qui dcrit les implmentations du look and feel install.
static setLookAndFeel(String className)

Dnit le look and feel actuel, laide du nom de classe donn (comme "javax.swing.plaf. metal.MetalLookAndFeel").

Chapitre 8

Gestion des vnements

341

javax.swing.UIManager.LookAndFeelInfo 1.2

String getName()

Renvoie le nom dafchage pour le look and feel.


String getClassName()

Renvoie le nom de la classe dimplmentation pour le look and feel.

Classes adaptateurs
Tous les vnements ne sont pas aussi simples grer que les clics de boutons. Dans un programme professionnel, vous devez surveiller le moment o lutilisateur tente de fermer le cadre principal car il ne doit pas perdre de travail. Vous pouvez souhaiter afcher une bote de dialogue lorsque lutilisateur ferme le cadre et ne sortir quaprs conrmation 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 du cadre :
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 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 signie que les sept mthodes doivent tre implmentes. Une seule nous intresse en loccurrence : la mthode windowClosing. Nous pouvons bien sr dnir 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)

342

Au cur de Java 2 - Notions fondamentales

{ if (lutilisateur accepte) 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) {}

Il est fastidieux dcrire les signatures de six mthodes qui ne font rien. Pour simplier 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 signie 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 an de spcier 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). Protons 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) { if (lutilisateur accepte) 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.5). Six dentre elles ne feront rien ; la mthode windowClosing appelle System.exit(0) pour terminer lexcution de lapplication.
ATTENTION
Si vous avez mal orthographi le nom dune mthode lors de lextension dune classe dadaptateur, le compilateur ninterceptera pas votre erreur. Si vous dnissez par exemple une mthode windowIsClosing dans une classe WindowAdapter, vous obtenez une classe avec huit mthodes et la mthode windowClosing ne fait rien.

Chapitre 8

Gestion des vnements

343

Figure 8.5
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) { if (lutilisateur accepte) System.exit(0); } });

Voici les oprations effectues par cet extrait de code :


m m m m m

Il dnit une classe anonyme qui tend la classe WindowAdapter. 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.

344

Au cur de Java 2 - Notions fondamentales

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

Actions
Il existe souvent plusieurs moyens dactiver la mme commande. Lutilisateur peut slectionner une fonction par lintermdiaire dun menu, dun raccourci clavier ou dun bouton dans une barre

Chapitre 8

Gestion des vnements

345

doutils. Lopration est simple raliser dans le modle dvnement AWT : il suft de lier tous les vnements au mme couteur. Supposons par exemple que blueAction soit un couteur daction dont la mthode actionPerformed colorie larrire-plan en bleu. Vous pouvez attacher le mme objet comme couteur de plusieurs sources dvnement :
m m m

un bouton de la barre doutils libell "Blue" ; une option de menu baptise "Blue" ; une combinaison de touches Ctrl+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 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).
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)

Une interface Action possde les mthodes suivantes :

La premire mthode est la mthode habituelle de linterface ActionListener : en fait, linterface Action est drive dActionListener. 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 vrier 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 prdnies 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.1 dcrit les noms des actions prdnies. Si lobjet Action est ajout un menu ou une barre doutils, le nom et licne sont automatiquement rcuprs et afchs dans loption du menu ou sur le bouton de la barre doutils. La valeur de SHORT_DESCRIPTION safche dans une bulle daide. Les deux dernires mthodes de linterface Action permettent aux autres objets en particulier les menus ou les barres doutils qui ont dclench laction de recevoir une notication lorsque les proprits de lobjet Action sont modies. 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 afcher en gris la rubrique correspondant laction. Les couteurs de

346

Au cur de Java 2 - Notions fondamentales

changement de proprit sont une construction gnrique intgre au modle de composant des Java beans. Vous en apprendrez plus sur les beans et leurs proprits dans le Volume II.
Tableau 8.1 : Noms des actions prdnies

Nom NAME SMALL_ICON SHORT_DESCRIPTION LONG_DESCRIPTION MNEMONIC_KEY ACCELERATOR_KEY ACTION_COMMAND_KEY DEFAULT

Valeur Nom de laction ; afch sur les boutons et les options de menu. Emplacement de stockage dune petite icne ; pour afchage sur un bouton, une option de menu ou dans la barre doutils. Courte description de licne ; pour afchage dans une bulle daide. Description dtaille de licne ; pour utilisation potentielle dans laide en ligne. Aucun composant Swing nutilise cette valeur. Abrviation mnmonique ; pour afchage dans une option de menu (voir Chapitre 9). Emplacement pour le stockage dune pression au clavier. Aucun composant Swing nutilise cette valeur. Utilise dans la mthode registerKeyboardAction, maintenant obsolte. Proprit fourre-tout. Aucun composant Swing nutilise cette valeur.

Notez quAction 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 lui fournirons le nom de la commande, une icne et la couleur souhaite. Nous stockerons la couleur dans la table des couples nom/valeur propose par la classe AbstractAction. Voici le code de la classe ColorAction. Le constructeur spcie le couple nom/valeur et la mthode actionPerformed se charge de modier 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()); } public void actionPerformed(ActionEvent event) { Color c = (Color) getValue("color");

Chapitre 8

Gestion des vnements

347

buttonPanel.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 dnit laction en tant qucouteur. Vous pouvez voir licne et la bulle daide la Figure 8.6. Vous verrez au prochain chapitre quil est aussi trs simple dajouter la mme action un menu.
Figure 8.6
Les boutons afchent 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. 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 statique getKeyStroke de la classe KeyStroke.
KeyStroke ctrlBKey = KeyStroke.getKeyStroke("ctrl B");

Pour comprendre la prochaine tape, vous devez connatre la notion de focus du clavier. Une interface utilisateur peut avoir plusieurs boutons, menus, barres de dlement et autres composants. Lorsque vous appuyez sur une touche, le composant qui a le focus est sollicit. Ce composant est gnralement visuellement diffrenciable. Ainsi, dans le look and feel Java, un bouton ayant le focus prsente une ne bordure rectangulaire autour de son libell. Vous pouvez utiliser la touche Tab pour dplacer le focus entre les composants. Ds que vous appuyez sur la barre despacement, cela revient cliquer sur le bouton ayant le focus. Dautres touches ralisent dautres actions, notamment les touches ches qui permettent de dplacer une barre de dlement. Toutefois, ici, nous ne voulons pas envoyer la pression de touche au composant qui a le focus. Sinon, chacun des boutons devrait savoir comment grer les 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. Chaque JComponent a trois affectations dentre qui associent les objets KeyStroke aux actions. Les trois affectations correspondent trois conditions diffrentes (voir Tableau 8.2).

348

Au cur de Java 2 - Notions fondamentales

Le traitement des frappes au clavier vrie ces affectations dans lordre suivant : 1. Vrie 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, vrie les indicateurs WHEN_ ANCESTOR_OF_FOCUSED_COMPONENT de ses composants parent. 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 able, si une combinaison de touches apparat dans plusieurs indicateurs WHEN_IN_FOCUSED_WINDOW.
Tableau 8.2 : Conditions daffectations dentre

Indicateur WHEN_FOCUSED WHEN_ANCESTOR_OF_FOCUSED_COMPONENT WHEN_IN_FOCUSED_WINDOW

Invoquer laction Quand ce composant a le focus clavier Quand ce composant contient le composant qui a le focus clavier Quand ce composant se trouve dans la mme fentre que le composant qui a le focus clavier

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 signie 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. InputMap naffecte pas directement les objets KeyStroke aux objets Action. Lassignation est faite vers des objets arbitraires, et une seconde affectation, implmente par la classe ActionMap, attribue 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);

Chapitre 8

Gestion des vnements

349

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 afch 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 afchs.

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. Enn, extrayez laffectation dentre du composant de plus haut niveau. Ajoutez la paire (touche daction, objet action) laffectation. Le Listing 8.3 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 modie la couleur du panneau.
Listing 8.3 : ActionTest.java
import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * @version 1.33 2007-06-12 * @author Cay Horstmann */ public class ActionTest { public static void main(String[] args) {

350

Au cur de Java 2 - Notions fondamentales

EventQueue.invokeLater(new Runnable() { public void run() { 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); buttonPanel = new JPanel(); // 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); Action redAction = new ColorAction("Red", new ImageIcon("red-ball.gif"), Color.RED); // ajoute des boutons pour ces actions buttonPanel.add(new JButton(yellowAction)); buttonPanel.add(new JButton(blueAction)); buttonPanel.add(new JButton(redAction)); // ajoute un panneau au cadre add(buttonPanel); // associe les touches Y, B et R aux noms InputMap imap = buttonPanel.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"); // associe les noms aux actions ActionMap amap = buttonPanel.getActionMap(); amap.put("panel.yellow", yellowAction); amap.put("panel.blue", blueAction); amap.put("panel.red", redAction); } public class ColorAction extends AbstractAction {

Chapitre 8

Gestion des vnements

351

/** * Construit une action de couleur. * @param name Le nom afficher sur le bouton * @param icon Licne afficher sur le bouton * @param c La couleur de fond */ 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"); buttonPanel.setBackground(c); } } private JPanel buttonPanel; public static final int DEFAULT_WIDTH = 300; public static final int DEFAULT_HEIGHT = 200; } javax.swing.Action 1.2

void setEnabled(boolean b) boolean isEnabled()

Rcuprent ou dnissent la proprit enabled de cette action.


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 prdnies (voir Tableau 8.1). Lobjet associ au nom.

value

Object getValue(String key)

Renvoie la valeur extraite dun couple nom/valeur.


javax.swing.KeyStroke 1.2

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. La description dmarre par zro, un ou plusieurs modicateurs shift control ctrl meta alt altGraph et se termine par la chane typed, suivie dune chane dun seul caractre, par exemple, "typed a" ou dun spcicateur dvnement optionnel (pressed, par dfaut, ou released, suivi dun code de touche). Le code de touche, lorsquil est prx par VK_, doit correspondre une constante KeyEvent; ainsi, "INSERT" correspond KeyEvent.VK_INSERT.

352

Au cur de Java 2 - Notions fondamentales

javax.swing.JComponent 1.2

ActionMap getActionMap() 1.3

Renvoie laffectation daction qui attribue les frappes de touches (qui peuvent tre des objets arbitraires) aux touches daction.

InputMap getInputMap(int flag) 1.3

Extrait laffectation dentre qui attribue les pressions de touches aux cls dobjets action. Paramtres : flag Une condition du focus de saisie pour dclencher laction, dont les valeurs gurent au Tableau 8.2.

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 rgies de faon interne par les divers composants de linterface utilisateur. 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. Nous allons vous proposer un diteur graphique simpli qui permettra lutilisateur de placer, de dplacer et deffacer des carrs sur une grille (voir Figure 8.7).
Figure 8.7
Un programme de test de la souris.

Lorsque lutilisateur clique avec un bouton de la souris, trois mthodes de lcouteur sont appeles : mousePressed quand le bouton est enfonc, mouseReleased quand il est relch et, enn, 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 inigent leurs utilisateurs des combinaisons de clic de souris et de touches, comme Ctrl+Maj+clic. Cette technique est, selon nous, assez critiquable mais, si cela vous plat, vous dcouvrirez que linterception des modications de boutons de souris et de clavier est trs complique. Les masques de bits permettent de tester les modicateurs qui ont t dnis. Dans lAPI dorigine, deux des masques de bouton galent deux masques de modication de clavier, savoir :
BUTTON2_MASK == ALT_MASK BUTTON3_MASK == META_MASK

Chapitre 8

Gestion des vnements

353

Cela permet aux utilisateurs qui disposent dune souris un seul bouton de simuler la prsence dautres boutons en maintenant enfonces des touches de modication. Toutefois, depuis Java SE 1.4, une nouvelle mthode 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 modicateurs 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 an 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()); } public void mouseClicked(MouseEvent event) { current = find(event.getPoint()); if (current!= null && event.getClickCount() >= 2) remove(current); }

Quand le curseur de la souris passe au-dessus dune fentre, celle-ci reoit un ot dvnements de mouvement de souris. Vous voyez quil y a des interfaces MouseListener et MouseMotionListener. Cela se fait des ns defcacit : on trouve de nombreux vnements de souris lorsque lutilisateur dplace celle-ci et un couteur qui ne se proccupe que des clics ne sera pas intress par des dplacements de souris indsirables. Notre programme de dmonstration intercepte malgr tout ces vnements an 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.3 prsente les constantes utilisables avec cette mthode et les pointeurs tels quils apparaissent sous Windows.

354

Au cur de Java 2 - Notions fondamentales

Tableau 8.3 : Exemples de pointeurs

Icne

Constante DEFAULT_CURSOR

Icne

Constante NE_RESIZE_CURSOR

CROSSHAIR_CURSOR

E_RESIZE_CURSOR

HAND_CURSOR

SE_RESIZE_CURSOR

MOVE_CURSOR TEXT_CURSOR WAIT_CURSOR N_RESIZE_CURSOR

S_RESIZE_CURSOR SW_RESIZE_CURSOR W_RESIZE_CURSOR NW_RESIZE_CURSOR

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

INFO
Vous pouvez dnir 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 spcie le dcalage de son "point chaud". Le troisime est une chane qui le dcrit. Elle peut tre employe pour le support daccessibilit, par exemple, an 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

Chapitre 8

Gestion des vnements

355

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 dnis dans une interface spare appele MouseMotionListener. Dans notre programme, les deux types dvnements de la souris nous intressent. Deux classes internes sont dnies : MouseHandler et MouseMotionHandler. La classe MouseHandler tend la classe MouseAdapter, car elle ne dnit que deux des cinq mthodes de MouseListener. La classe MouseMotionHandler implmente MouseMotionListener et dnit les deux mthodes de cette interface. Le programme est dcrit dans le Listing 8.4.
Listing 8.4 : MouseTest.java
import import import import import java.awt.*; java.awt.event.*; java.util.*; java.awt.geom.*; javax.swing.*;

/** * @version 1.32 2007-06-12 * @author Cay Horstmann */

356

Au cur de Java 2 - Notions fondamentales

public class MouseTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { 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 composant au cadre MouseComponent component = new MouseComponent(); add(component); } public static final int DEFAULT_WIDTH = 300; public static final int DEFAULT_HEIGHT = 200; } /** * Un composant avec des oprations de la souris * pour lajout et la suppression de carrs. */ class MouseComponent extends JComponent { public MouseComponent() { squares = new ArrayList<Rectangle2D>(); current = null; addMouseListener(new MouseHandler()); addMouseMotionListener(new MouseMotionHandler()); } public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D)g; // dessiner tous les carrs for (Rectangle2D r: squares) g2.draw(r); }

Chapitre 8

Gestion des vnements

357

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

358

Au cur de Java 2 - Notions fondamentales

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

int getClickCount()

Renvoie le nombre de clics conscutifs associs lvnement (lintervalle qui dtermine deux clics "conscutifs" dpend du systme).

Chapitre 8

Gestion des vnements

359

java.awt.event.InputEvent 1.1

int getModifiersEx() 1.4

Renvoie les modicateurs 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 modicateurs 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 hotSpot name Limage afcher lorsque le pointeur est actif. Le point chaud du pointeur (par exemple lextrmit dune che ou le centre dune croix). 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 prdnis spcis par le paramtre cursor.

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.8 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).

360

Au cur de Java 2 - Notions fondamentales

Figure 8.8
Schma de lhritage des classes dvnements AWT.
Event Object

AWT Event

Action Event

Adjustment Event

Component Event

Item Event

Focus Event

Input Event

Paint Event

Window Event

Key Event

Mouse Event

MouseWheel Event

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 le 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, an de fournir les systmes de saisie des langues idographiques, les robots de test automatis, etc. Nous ne traiterons pas de ces types dvnements spcialiss.

Chapitre 8

Gestion des vnements

361

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, puis un relchement du bouton de la souris (mais seulement si le pointeur se trouve encore dans la zone du bouton afch sur lcran). Ce peut tre galement une pression sur une touche, 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 dlement 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 sur un 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 dlement). ItemEvent (lutilisateur a fait une slection dans un groupe de cases cocher ou dans une liste). 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). WindowEvent (ltat de la fentre a chang).
ActionListener AdjustmentListener FocusListener ItemListener KeyListener MouseListener MouseMotionListener MouseWheelListener WindowListener WindowFocusListener WindowStateListener

m m

Il y a cinq classes dvnements de bas niveau :


m m

m m m

Les interfaces suivantes permettent dcouter ces vnements. :

Certaines interfaces couteur AWT celles qui possdent plusieurs mthodes sont accompagnes dune classe adaptateur qui implmente toutes les mthodes de linterface an 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

Le Tableau 8.4 montre les interfaces couteur, les vnements et les sources dvnements les plus importants de AWT.

362

Au cur de Java 2 - Notions fondamentales

Tableau 8.4 : Rsum de la gestion des vnements

Interface
ActionListener

Mthodes
actionPerformed

Paramtres/ accesseurs
ActionEvent getActionCommand getModifiers AdjustmentEvent getAdjustable getAdjustmentType getValue ItemEvent getItem getItemSelectable getStateChange FocusEvent
isTemporary

Evnements gnrs par


AbstractButton JComboBox JTextField Timer JScrollbar

AdjustmentListener

adjustmentValueChanged

ItemListener

itemStateChanged

AbstractButton JComboBox

FocusListener KeyListener

focusGained focusLost keyPressed keyReleased keyTyped

Component Component

KeyEvent
getKeyChar getKeyCode getKeyModifiersText getKeyText isActionKey

MouseListener

mousePressed mouseReleased mouseEntered mouseExited mouseClicked mouseDragged mouseMoved mouseWheelMoved

MouseEvent
getClickCount getX getY getPoint translatePoint

Component

MouseMotionListener MouseWheelListener

MouseEvent MouseWheelEvent getWheelRotation getScrollAmount

Component Component

Chapitre 8

Gestion des vnements

363

Tableau 8.4 : Rsum de la gestion des vnements

Interface
WindowListener

Mthodes
windowClosing windowOpened windowIconified windowDeiconified windowClosed windowActivated windowDeactivated windowGainedFocus windowLostFocus windowStateChanged

Paramtres/ accesseurs
WindowEvent
getWindow

Evnements gnrs par


Window

WindowFocusListener WindowStateListener

WindowEvent
getOppositeWindow

Window Window

WindowEvent getOldState getNewState

Cela clt notre examen de la gestion des vnements AWT. Le chapitre suivant montre comment rassembler les composants les plus communs proposs par Swing, avec une description dtaille des vnements quils gnrent.

9
Swing et les composants dinterface utilisateur
Au sommaire de ce chapitre

Swing et larchitecture Modle-Vue-Contrleur 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 efcacement 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 nir, 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 dans le Volume II.

366

Au cur de Java 2 - Notions fondamentales

Swing et larchitecture Modle-Vue-Contrleur


Comme nous lindiquions, nous dmarrons ce chapitre par une section dcrivant larchitecture des composants Swing. Nous verrons dabord la notion de