Résumé175exercicescorrigéspourmaîtriserJavaConçu pour les étudiants en informatique, ce recueil d’exercices corrigés est lecomplémentidéaldeProgrammerenJavadumêmeauteuroudetoutautreouvraged’initiationaulangageJava.
CettenouvelleéditiontientcomptedesnouveautésdeJava8,publiéenmars2014,avecunnouveauchapitresurlesexpressionslambdaetlesstreams.
Les175exercicessontclassésparthèmesen18chapitres.Chaquechapitredébuteparlalistedesnotionsnécessairesàlarésolutiondesexercices(sectionPrérequis).Certainsexercicesportentsurunenotionpréciseindiquéedansl’énoncé.D’autres,appelésexercicesdesynthèse, fontappelà lamiseenœuvredeplusieursnotionsétudiéesdanslesexercicesouchapitresprécédents,etnécessitentdoncuneffortderéflexionplusfourni.
Chaque énoncé d’exercice est suivi d’une ou plusieurs solutions détaillées. Leurcodesourceestfournisurlesitewww.editions-eyrolles.com.
AusommaireLesopérateursetexpressions(10exercices)•Lesinstructionsdecontrôle:if,switch, for,while,do…while (12exercices) •Lesclasseset lesobjetsJava(23exercices)•Lestableaux(14exercices) •L’héritageet lepolymorphisme(14exercices) •La classeString et les chaînesde caractères (9exercices) •Lestypesénumérés(7exercices)•Lagestiondesexceptions(10exercices) •Lesbasesdelaprogrammationévénementielle(12exercices)•LesprincipauxcontrôlesdeSwing(9exercices)•Lesboîtesdedialogue(6exercices) •Lesmenuset lesactions(7exercices) •Lesévénementsdebasniveau :sourisetclavier (8 exercices) • Les applets Java (6 exercices) • Les fichiers (5exercices)•Lesgénériques(10exercices)•Lescollections(5exercices)•Lesexpressions lambda et les streams (9exercices).Annexes. Les constantes etfonctionsmathématiques•Lescomposantsgraphiquesetleursméthodes•Lesévénementsetleursécouteurs•LaclasseClavier.
BiographieauteurClaudeDelannoyIngénieurinformaticienauCNRS,ClaudeDelannoypossèdeunegrandepratiquede
1
laformationcontinueetdel’enseignementsupérieur.Réputéspourlaqualitédeleurdémarchepédagogique,sesouvragessurleslangagesetlaprogrammationtotalisentplusde300000exemplairesvendus.
www.editions-eyrolles.com
2
ClaudeDelannoy
ExercicesenJava
4eédition
Troisièmetirage2017,avecnouvelleprésentation
3
ÉDITIONSEYROLLES61,bdSaint-Germain75240ParisCedex05
www.editions-eyrolles.com
AUXÉDITIONSEYROLLES
Dumêmeauteur
C.DELANNOY.–ProgrammerenJava.Java5à8.N°11889,9eédition,2014,948pages(rééditionavecnouvelleprésentation,2016).
C.DELANNOY.–ProgrammerenlangageC++.N°14008,8eédition,2011,820pages.
C.DELANNOY.–ExercicesenlangageC++.N°12201,3eédition,2007,336pages(rééditionavecnouvelleprésentation,2016).
C.DELANNOY.–LeguidecompletdulangageC.N°14020,2014,844pages.
C.DELANNOY.–S’initieràlaprogrammationetàl’orientéobjet.AvecdesexemplesenC,C++,C#,Python,JavaetPHP.N°14011,2eédition,septembre2014,360pagesenviron.
Enapplicationdelaloidu11mars1957,ilestinterditdereproduireintégralementoupartiellementleprésentouvrage,surquelquesupportquecesoit,sansl’autorisationdel’ÉditeurouduCentreFrançaisd’exploitationdudroitdecopie,20,ruedesGrandsAugustins,75006Paris.
Laquatrièmeéditionduprésentouvrageestparueen2014sousl’ISBN978-2-212-14009-5.Àl’occasiondecetroisièmetirage,ellebénéficied’unenouvellecouverture.Letexteresteinchangé.
©GroupeEyrolles,2001-2014,pourletextedelaprésenteédition.©GroupeEyrolles,2017,ISBN:978-2-212-67385-2.
4
Tabledesmatières
Avant-propos
1.LesopérateursetlesexpressionsExercice1.PrioritésdesopérateursarithmétiquesetparenthèsesExercice2.ConversionsimplicitesExercice3.ExceptionsflottantesetconventionsIEEE754Exercice4.LetypecharExercice5.Opérateurslogiquesà"courtcircuit"Exercice6.PrioritésdesopérateursExercice7.AffectationetconversionExercice8.Opérateursd’incrémentation,dedécrémentationetd’affectationélargieExercice9.Opérateursd’incrémentationetd’affectationélargieExercice10.Opérateurconditionnel
2.LesinstructionsdecontrôleExercice11.SyntaxedeifetdeswitchExercice12.Rôledel’instructionswitchExercice13.SyntaxedesbouclesExercice14.Comparaisonentrefor,whileetdo…whileExercice15.RupturedeséquenceavecbreaketcontinueExercice16.Bouclewhile,opérateursd’affectationélargieetd’incrémentation(1)Exercice17.Bouclewhile,opérateursd’affectationélargieetd’incrémentation(2)Exercice18.Syntaxegénéraledestroispartiesd’uneboucleforExercice19.Synthèse:calculd’unesuitederacinescarréesExercice20.Synthèse:calculdelavaleurd’unesérie
5
Exercice21.Synthèse:dessind’untriangleenmodetexteExercice22.Synthèse:calculdecombinaisons
3.LesclassesetlesobjetsExercice23.Créationetutilisationd’uneclassesimpleExercice24.Initialisationd’unobjetExercice25.ChampsconstantsExercice26.Affectationetcomparaisond’objetsExercice27.Méthodesd’accèsauxchampsprivésExercice28.Conversionsd’argumentsExercice29.Champsetméthodesdeclasse(1)Exercice30.Champsetméthodesdeclasse(2)Exercice31.Champsetméthodesdeclasse(3)Exercice32.Blocd’initialisationstatiqueExercice33.SurdéfinitiondeméthodesExercice34.Recherched’uneméthodesurdéfinie(1)Exercice35.Recherched’uneméthodesurdéfinie(2)Exercice36.Recherched’uneméthodesurdéfinie(3)Exercice37.Surdéfinitionetdroitsd’accèsExercice38.EmploidethisExercice39.RécursivitédesméthodesExercice40.Modedetransmissiondesargumentsd’uneméthodeExercice41.ObjetsmembresExercice42.Synthèse:repèrescartésiensetpolairesExercice43.Synthèse:modificationdel’implémentationd’uneclasseExercice44.Synthèse:vecteursàtroiscomposantesExercice45.Synthèse:nombressexagésimaux
4.LestableauxExercice46.DéclarationetinitialisationdetableauExercice47.Utilisationusuelled’untableau(1)
6
Exercice48.Utilisationusuelled’untableau(2)Exercice49.Affectationdetableaux(1)Exercice50.Affectationdetableaux(2)Exercice51.Affectationdetableaux(3)Exercice52.Tableauenargument(1)Exercice53.Tableauenargument(2)Exercice54.TableauenvaleurderetourExercice55.TableauxdetableauxExercice56.Synthèse:nombresaléatoiresethistogrammeExercice57.Synthèse:calculvectorielExercice58.Synthèse:utilitairespourdestableauxdetableauxExercice59.Synthèse:cribled’Eratosthène
5.L’héritageetlepolymorphismeExercice60.Définitiond’uneclassedérivée,droitsd’accès(1)Exercice61.Définitiond’uneclassedérivée,droitsd’accès(2)Exercice62.HéritageetappelsdeconstructeursExercice63.RedéfinitionExercice64.Constructionetinitialisationd’uneclassedérivéeExercice65.DérivationssuccessivesetredéfinitionExercice66.DérivationssuccessivesetsurdéfinitionExercice67.LesbasesdupolymorphismeExercice68.PolymorphismeetsurdéfinitionExercice69.LeslimitesdupolymorphismeExercice70.ClasseabstraiteExercice71.ClasseabstraiteetpolymorphismeExercice72.InterfaceExercice73.Synthèse:comparaisonentrehéritageetobjetmembre
6.LaclasseStringetleschaînesdecaractèresExercice74.Constructionetaffectationdechaînes
7
Exercice75.Accèsauxcaractèresd’unechaîneExercice76.Conversiond’unentierenchaîneExercice77.Comptagedesvoyellesd’unmotExercice78.ArgumentsdelalignedecommandeExercice79.RedéfinitiondetoStringExercice80.Synthèse:conjugaisond’unverbeExercice81.Synthèse:tridemotsExercice82.Synthèse:gestiond’unrépertoire
7.LestypesénumérésExercice83.Définitionetutilisationd’untypeénumérésimpleExercice84.Itérationsurlesvaleursd’untypeénuméréExercice85.Accèsparleurrangauxvaleursd’untypeénuméré(1)Exercice86.Lecturedevaleursd’untypeénuméréExercice87.Ajoutdeméthodesetdechampsàuneénumération(1)Exercice88.Ajoutdeméthodesetdechampsàuneénumération(2)Exercice89.Synthèse:gestionderésultatsd’examens
8.LesexceptionsExercice90.Déclenchementettraitementd’uneexceptionExercice91.Transmissiond’informationaugestionnaireExercice92.CheminementdesexceptionsExercice93.CheminementdesexceptionsetchoixdugestionnaireExercice94.CheminementdesexceptionsExercice95.InstructionreturndansungestionnaireExercice96.Redéclenchementd’uneexceptionetchoixdugestionnaireExercice97.BlocfinallyExercice98.RedéclenchementetfinallyExercice99.Synthèse:entiersnaturels
9.Lesbasesdelaprogrammationévénementielle
8
Exercice100.Écouteursdeclicsd’unefenêtreExercice101.ÉcouteursdeclicsdeplusieursfenêtresExercice102.ÉcouteurcommunàplusieursfenêtresExercice103.Créationdeboutonsetchoixd’ungestionnaireFlowLayoutExercice104.Gestiondeplusieursboutonsd’unefenêtreavecunseulécouteurExercice105.Synthèse:créationetsuppressiondeboutons(1)Exercice106.Synthèse:créationetsuppressiondeboutons(2)Exercice107.DessinpermanentdansunefenêtreExercice108.Synthèse:dessinpermanentetchangementdecouleurExercice109.Synthèse:dessinpermanent,colorationetadaptationàlatailled’unefenêtreExercice110.DessinàlavoléeExercice111.Synthèse:ardoisemagiqueencouleur
10.LesprincipauxcontrôlesdeSwingExercice112.CasesàcocherExercice113.CasesàcocherennombrequelconqueExercice114.BoutonsradioennombrequelconqueExercice115.ChampsdetexteExercice116.ChampdetexteetévénementsActionetFocusExercice117.Écoutepermanented’unchampdetexteExercice118.Synthèse:sérieharmoniqueExercice119.Gestiond’uneboîtedelisteExercice120.Synthèse:pendule
11.LesboîtesdedialogueExercice121.UtilisationdeboîtesdemessageetdeconfirmationExercice122.Utilisationdeboîtesdemessage,deconfirmationetdesaisieExercice123.Programmationd’uneboîtedemessageExercice124.Programmationd’uneboîtedeconfirmationExercice125.Programmationd’uneboîtedesaisie
9
Exercice126.Synthèse:saisied’uneheure
12.LesmenusExercice127.Créationd’unmenudéroulantusuelExercice128.Gestiondesactionssurlesoptionsd’unmenuExercice129.Activation,désactivationd’optionsExercice130.Synthèse:calculssurdesrectanglesExercice131.Synthèse:colorationparboutonsradioExercice132.Synthèse:choixdecouleurdefondetdeformepardesmenuscomposésExercice133.Synthèse:choixdecouleursetdedimensionspardesmenussurgissants
13.LesévénementsdebasniveauExercice134.IdentificationdesboutonsdelasourisExercice135.Vraisdoubles-clicsExercice136.Suividesdéplacementsdelasouris(1)Exercice137.Suividesdéplacementsdelasouris(2)Exercice138.Dessinparleclavier(1)Exercice139.Synthèse:dessinparleclavier(2)Exercice140.Sélectiond’uncomposantparleclavierExercice141.Miseenévidenced’uncomposantsélectionné
14.LesappletsExercice142.Comptagedesarrêtsd’uneappletExercice143.DessindansuneappletExercice144.Synthèse:dessinparamétrédansuneappletExercice145.Synthèse:tracédecourbedansuneappletExercice146.Différencesentreappletetapplication
15.LesfluxetlesfichiersExercice147.Créationséquentielled’unfichierbinaireExercice148.Listeséquentielled’unfichierbinaireExercice149.Synthèse:consultationd’unrépertoireenaccèsdirect
10
Exercice150.Synthèse:listed’unfichiertexteavecnumérotationdeslignesExercice151.Listed’unrépertoire
16.LaprogrammationgénériqueExercice152.ClassegénériqueàunparamètredetypeExercice153.ClassegénériqueàplusieursparamètresdetypeExercice154.Conséquencesdel’effacement(1)Exercice155.Conséquencesdel’effacement(2)Exercice156.MéthodegénériqueàunargumentExercice157.MéthodegénériqueeteffacementExercice158.DérivationdeclassesgénériquesExercice159.Lesdifférentessortesderelationd’héritageExercice160.Limitationsdesparamètresdetyped’uneméthodeExercice161.RedéfinitiondelaméthodecompareTo
17.LescollectionsetlestablesassociativesExercice162.Dépendanceouindépendanced’unitérateurExercice163.Manipulationd’untableaudetypeArrayListExercice164.Trid’unecollection(1)Exercice165.Trid’unecollection(2)Exercice166.Réalisationd’unelistetriéeenpermanenceExercice167.Créationd’unindexExercice168.Inversiond’unindex
18.LesexpressionslambdaetlesstreamsExercice169.LambdaetinterfacesprédéfiniesExercice170.LambdaetréférencesExercice171.L’interfaceComparatorExercice172.LesméthodesusuellesdesstreamsExercice173.TraitementdelisteavecunstreamExercice174.Répertoire
11
Exercice175.Répertoire(bis)Exercice176.ReduceExercice177.CollectetCollectors
A.Lesconstantesetfonctionsmathématiques
B.LescomposantsgraphiquesetleursméthodesExercice1.LesclassesdecomposantsExercice2.Lesméthodes
C.LesévénementsetlesécouteursExercice3.LesévénementsdebasniveauExercice4.LesévénementssémantiquesExercice5.Lesméthodesdesévénements
D.LaclasseClavier
12
Avant-propos
Quel’onsoitdébutantouprogrammeurchevronné,lamaîtrised’unnouveaulangagedeprogrammationpasseobligatoirementparlapratique.CetouvrageestdestinéàaccompagneretàprolongervotreétudedeJava.Sastructurecorrespondà laprogressionclassiqued’uncours : lesopérateurset lesexpressions,les instructions de contrôle, les classes et les objets, les tableaux, l’héritage et lepolymorphisme, la classeString, les types énumérés, les exceptions, les bases de laprogrammation événementielle, les principaux contrôles de Swing, les boîtes dedialogue, les menus, les événements de bas niveau, les applets, les fichiers, laprogrammation générique, les collections et les tables associatives, les expressionslambdaetlesstreams.Endébutdechaquechapitre,voustrouverezlalistedesconnaissancesnécessairesàlarésolutiondesexercices.Cesconnaissancespeuventêtreacquisesàl’aidedumanuelProgrammerenJava,dumêmeauteur,oudetoutautreouvraged’apprentissagedecelangage.Nousavonsprévudeuxsortesd’exercices:lesexercicesd’applicationetlesexercicesdesynthèse.Chaqueexerciced’applicationaétéconçupourvousentraîneràmettreenœuvreuneouplusieursnotionsquisontclairementindiquéesdansl’intitulémêmedel’exercice.Nousavonstoutparticulièrementcherchéàéquilibrer larépartitiondecesexercices.D’une part, nous avons évité la prolifération d’exercices semblables sur un mêmethème.D’autrepart,nouscouvronslaplupartdesaspectsdulangage,qu’ils’agissedesfondementsdelaprogrammationorientéeobjetoudecaractéristiquesplustechniquesetplusspécifiquesàJava.Les exercices de synthèse, quant à eux, sont destinés à favoriser l’intégration desconnaissancesquevousapprendrezàmettreenœuvredansdescontextesvariés.Les
13
notionsàutilisern’étantindiquéesnidansl’intitulé,nidansl’énoncédecesexercicesdesynthèse,leurrésolutionvousdemanderaplusderéflexionquecelledesexercicesd’application.
L’ouvrage,J2SEetSwingSilesinstructionsdebasedeJavan’ontpratiquementpasévoluédepuissanaissance,iln’envapasdemêmedesesbibliothèquesstandards.Trèstôt,lemodèledegestiondesévénementsaété fortementmodifié (version1.1).Puis,denombreuxcomposantsgraphiques dits Swing sont apparus avec la version 1.2, renommée à cette occasionJ2SE(Java2StandardEdition).Unpeuplusrécemment,laversion5.0deJ2SE(diteaussi Java 5) a introduit d’importantes nouveautés, notamment la programmationgénérique et son application aux collections, la nouvelle boucle dite for… each, lestypesénumérés.Enfin,laversionJavaSE81(diteaussiJava8)aintroduit,entreautres,lesimportantesnotionsd’expressionslambdaetdestreams.Cettenouvelleéditionde l’ouvragese fondesur laversionJavaSE8.Laplupartdutemps,nousavonsfaitensorteque lescorrigésd’exercicesrestentcompatiblesavecles versions antérieures (y compris celles précédent Java 5), en utilisant descommentaires appropriés exprimant les différences éventuelles. Seuls font exceptionleschapitresrelatifsauxtypesénumérésetàlaprogrammationgénérique(quin’ontpasd’équivalentdanslesversionsantérieuresàJava5),lechapitrerelatifauxcollectionset aux tables associatives (ajouté dansuneprécédente édition), ainsi que le nouveauchapitredecettedernièreéditionrelatifauxexpressionslambdaetauxstreams.Par ailleurs, et conformément aux recommandations d’Oracle, nous nous appuyonsentièrementsurlescomposantsSwingintroduitsavecJava2,ceciaussibienpourlesapplicationsautonomesquepourlesapplets.
LaclasseClavierAlorsqueJavadisposedeméthodesd’affichaged’informationdanslafenêtreconsole,rien n’est prévu pour la lecture au clavier.Bien entendu, il est toujours possible dedéveloppersoi-mêmeuneclasseoffrant lesservicesdebasequesont la lectured’unentier,d’unflottant,d’uncaractèreoud’unechaîne.Pourvousfaciliterlarésolutiondecertains exercices, vous trouverezune telle classe (nomméeClavier.java) sur le siteWebd’accompagnement;salisteestégalementfournieenAnnexeD.SesméthodessenommentlireChar,lireInt,lireFloat,lireDoubleetlireString.Parexemple,pourlireunevaleurentièreetlaplacerdanslavariablenb,vouspourrezprocéder ainsi (notez bien que les parenthèses sont obligatoires dans l’appel d’uneméthodesansarguments):n=Clavier.lireInt();
Notez que, depuis Java 5, il existe une classe nommée Scanner qui offre des
14
possibilitésd’analysedechaînesdecaractères.Parexemple,avec:Scannerclavier=newScanner(System.in);
on construit un objet clavier associé à l’entrée standard System.in. La lecture desinformations peut alors se faire à l’aide de méthodes telles que nexInt, nextFloat,nextDoubledelaclasseScanner.Parexemple:
doubleht=clavier.nextDouble();
Nous avons ici préféré éviter de recourrir à ces possibilités, car le formatage desinformations y fait appel à certaines caractéristiques dites de « localisation »spécifiquesàchaquepays.
LesiteWebd’accompagnementLecodesourcedescorrigésd’exercicesestfournisurlesiteWebd’accompagnementàl’adressewww.editions-eyrolles.com. Pour accéder à l’espace de téléchargement, ilvous suffit de taper le nom de l’auteur (Delannoy) dans le formulaire de rechercherapideetdesélectionnerl’ouvrageExercicesenJava.Il existe souventplusieursmanièresde résoudre lemêmeexerciceet il sepeutdoncquevotresolutiondiffèredecelleprésentéedanslecorrigésansêtreincorrectepourautant.Encasdedoute,vouspouvezcontacterl’auteurpare-mailàl’adressesuivante:[email protected].
1.LadénominationdesdifférentesversionsdeJavaaévoluéavecle tempsdeJDKxxàJavaSExx, enpassantparJ2SExx,maisonparleaussideJavaxx
15
Chapitre1
Lesopérateursetlesexpressions
Connaissancesrequises
•Écritured’unprogrammeprincipal,c’est-à-direforméd’uneclassecomportantuneseuleméthodenomméemain
•Règlesgénéralesd’écriture:identificateurs,motsclés,séparateurs,formatlibre,commentaires
•Lestypesprimitifs:entiers(byte,short,intetlong),flottants(float,double),caractères(char)etbooléens(boolean).
•Déclarationdevariablesd’untypeprimitif;lespossibilitésd’initialisation;rôledefinal;notiond’expressionconstante
•Affichaged’informationsavecSystem.out.printetSystem.out.println
•Lesopérateursarithmétiques;conversionsimplicitesdanslesexpressions(ajustementdetype,promotionnumérique);comportementencasd’exception;existencedesvaleursInfinityetNaN
•Lesopérateursrelationnels;conversionsimplicitesdesopérandes
•Lesopérateurslogiques;casparticulierdesopérateursdits"decourt-circuit"&&et||
•Lesopérateursd’affectationsimpleouélargie;conversionsforcéesparaffectation
•Lesopérateursd’incrémentationetdedécrémentation
•L’opérateurdecast
16
1 Prioritésdesopérateursarithmétiquesetparenthèses
Éliminer les parenthèses superflues dans les expressions suivantes (l’ordre descalculsdevantresterlemême):
(a+b)-(2*c)//expression1
(2*x)/(y*z)//expression2
(x+3)*(n%p)//expression3
(-a)/(-(b+c))//expression4
(x/y)%(-z)//expression5
x/(y%(-z))//expression6
a+b-2*c//expression1
2*x/(y*z)//expression2
On pourrait aussi écrire cette expression 2*x/y/z mais l’ordre des calculs seradifférent,cequipeutavoirunelégèreincidencesurlerésultat.(x+3)*(n%p)//expression3
Ici aucune parenthèse ne peut être supprimée car * et% sont demême priorité ; lasuppressiondelasecondepairedeparenthèsesconduiraitàuneexpressionéquivalentà:((x+3)*n)%p.-a/-(b+c)//expression4
Ne pas oublier que l’opérateur unaire - est prioritaire sur tous les opérateursarithmétiquesàdeuxopérandes.x/y%-z//expression5
x/(y%-z)//expression6
17
2 Conversionsimplicites
Soitcesdéclarations:byteb1=10,b2=20;
shortp=200;
intn=500;
longq=100;
floatx=2.5f;
doubley=5.25;
Donnerletypeetlavaleurdesexpressionsarithmétiquessuivantes:b1+b2//1
p+b1//2
b1*b2//3
q+p*(b1+b2);//4
x+q*n//5
b1*q/x//6
b1*q*2./x//7
b1*q*2.f/x//8
b1+b2=30//1
L’opérateur+soumetlesvaleursdeb1etb2àlapromotionnumériquedebyteenint.Lerésutatestdetypeint.
p+b1=210//2
L’opérateur+soumetsesopérandesàdespromotionsnumériques:deshortenintpourpetdebyteenintpourb1.Lerésultatestdetypeint.
b1*b2=200//3
Làencore,avantd’effectuerleproduit, lesvaleursdeb1etdeb2 sontsoumisesà lapromotionnumériquedebyteenint.Lerésultatestdetypeint.
q+p*(b1+b2)=6100//4
On évalue tout d’abord la somme s=b1+b2, en soumettant les valeurs des deuxopérandesauxpromotionsnumériquesdebyteen int.Lavaleurdes est de type int.Puis on effectue la sommeq+p en soumettant le second opérande à une conversion
18
d’ajustementdetypedeshortenlong(typedeq).Lerésultatestdetypelong. Il fautmaintenant le multiplier par s, ce qui se fait en soumettant la valeur de s à uneconversiond’ajustementdetypedeintenlong.Lerésultatfinalestdetypelong.
x+q*n=50002.5//5
Onévalue toutd’abord leproduitq*nensoumettant lavaleurdenàuneconversiond’ajustementdetypedeintenlong.Lerésultatestdetypelong.Pourpouvoirl’ajouteràlavaleurdex,onlesoumetàuneconversiond’ajustementdetypedelongenfloat.Lerésultatestdetypefloat.
b1*q/x=400.0//6
Onévaluetoutd’abordlequotientq/xensoumettant lavaleurdeqàuneconversiond’ajustementde typede longen float.Le résultat estde type float.Pourpouvoir luiajouterlavaleurdeb1,onsoumetcettedernièreàuneconversiond’ajustementdetypedebyteen float (ou,cequi revientaumême,d’abordàunepromotionnumériquedebyteenint,puisàuneconversiond’ajustementdetypedeintenfloat).Lerésultatestdetypefloat.
b1*q*2./x=800.0//7
Onévaluetoutd’abordleproduitq*2.,ensoumettantlavaleurdeqàuneconversiond’ajustementdetypedelongendouble(attention,laconstante2.estdetypedoubleetnondetypefloat).Lerésultatestdetypedouble.Ilestdiviséparlavaleurobtenueparconversiond’ajustementdetypedexdefloatendouble.Lerésultat,detypedoubleestalorsmultiplié par la valeur obtenue par conversion d’ajustement de type de b1 endouble.Lerésultatestdetypedouble.
b1*q*2.f/x=800.0//8
Ils’agitdel’expressionprécédente,danslaquellelaconstante2.(detypedouble)estremplacéepar2.fdetypefloat.Lamêmedémarches’applique,ensubstituant le typefloatautypedouble.Lerésultatfinalestdetypefloat.
19
3 ExceptionsflottantesetconventionsIEEE754
Quelsrésultatsfournitceprogramme?publicclassExcep
{publicstaticvoidmain(Stringargs[])
{doublex1=1e200,x2=1e210;
doubley,z;
y=x1*x2;
System.out.println("valeurdey"+y);
x2=x1;
z=y/(x2-x1);
System.out.println (y + " divise par " + (x2-x1) + " = " +
z);
y=15;
z=y/(x2-x1);
System.out.println (y + " divise par " + (x2-x1) + " = " +
z);
z=(x2-x1)/(x2-x1);
System.out.println((x2-x1)+"divisepar"+(x2-x1)+"="
+z);
System.out.println(z+"+1="+(z+1));
x1=Float.POSITIVE_INFINITY;
x2=Double.NEGATIVE_INFINITY;
z=x1/x2;
System.out.println(x1+"/"+x2+"="+z);
}
}
Infinitydivisepar0.0=Infinity
15.0divisepar0.0=Infinity
20
0.0divisepar0.0=NaN
NaN+1=NaN
Infinity/-Infinity=NaN
Rappelons qu’en Java aucune opération sur les flottants ne conduit à un arrêt del’exécution.Enrevanche,lesnombresflottantsrespectentlesconventionsIEEE754quiimposent l’existence d’un motif particulier représentant les valeurs infinies, lequels’imprimesouslaformeInfinityou-Infinity.LesconstantescorrespondantessenotentFloat.InfinityouDouble.Infinity.Demême,ilexisteunmotifparticulierreprésentantune valeur non calculable ; il peut s’obtenir par Float.NaN ou Double.NaN et ils’imprimesouslaformeNaN.
21
4 Letypechar
Soitcesdéclarations:charc=60,ce='e',cg='g';
byteb=10;
Donnerletypeetlavaleurdesexpressionssuivantes:c+1
2*c
cg-ce
b*c
c+1=61
L’opérateur+soumet icisonpremieropérandeà lapromotionnumériquedecharenint,cequifournitlavaleur601.Lerésultatestdetypeint.
2*c=120
L’opérateur*soumeticisonsecondopérandeàlapromotionnumériquedecharenint,cequifournitlavaleur602.Lerésultatestdetypeint.
cg-ce=2
L’opérateur-soumeticisesdeuxopérandesàlapromotionnumériquedecharenint.Onobtientunrésultatdetypeintquireprésentel’écartentrelescodesdescaractèresgete (dans lecodeUnicode, les lettresconsécutivesd’unemêmecasseontdescodesconsécutifs).
b*c=600
L’opérateur*soumet icisesdeuxopérandesauxpromotionsnumériques :debyteenintpour lepremier,decharen int pour le second.Onnoteraqu’aucunproblèmededépassementdecapacitén’apparaîtpuisqueleproduitestbieneffectuédansletypeint(il en irait différemment s’il était effectué dans le type byte puisque 600 n’est pasreprésentabledanscetype).
22
5 Opérateurslogiquesà"courtcircuit"
Quelsrésultatsfournitceprogramme?publicclassCourCir
{publicstaticvoidmain(Stringargs[])
{inti=10,j=5;
if(i<5&&j++<10)System.out.println("&&1vrai");
elseSystem.out.println("&&1faux");
System.out.println("i="+i+"j="+j);
if(i<5&j++<10)System.out.println("&vrai");
elseSystem.out.println("&faux");
System.out.println("i="+i+"j="+j);
if(i<15&&j++<10)System.out.println("&&2vrai");
elseSystem.out.println("&&2faux");
System.out.println("i="+i+"j="+j);
if(i<15||j++<10)System.out.println("||vrai");
elseSystem.out.println("||faux");
System.out.println("i="+i+"j="+j);
}
}
&&1faux
i=10j=5
&faux
i=10j=6
&&2vrai
i=10j=7
||vrai
i=10j=7
23
Il faut simplement tenir compte de la proprité particulière dont bénéficient lesopérateurs&&et||ditsàcourt-circuit.Ilsn’évaluentleursecondopérandequelorsquecelaestnécessaire.
24
6 Prioritésdesopérateurs
Éliminerlesparenthèsessuperfluesdanslesexpressionssuivantes:a=(x+5)//1
a=(x=y)+2//2
a=(x=(y+2))//3
(a<b)&&(c<d)//4
(i++)*(n+p)//5
x+=(n%p)//6
n=(p+=5)//7
a=x+5//1
a=(x=y)+2//2
a=x=y+2//3
a<b&&c<d//4
i++*(n+p)//5
x+=n%p//6
n=(p+=5)//7
25
7 Affectationetconversion
Soitcesdéclarations:byteb;shortp;intn;longq;
finalintN=10;
floatx;doubley;
Parmi lesexpressionssuivantes, lesquellessont incorrectesetpourquoi?Lorsquel’expressionestcorrecte,citerlesconversionséventuellementmisesenjeu.
b=n//1
b=25//2
b=500//3
x=2*q//4
y=b*b//5
p=b*b//6
b=b+5//7
p=5*N-3//8
b=n;//1Erreur
Laconversiondeintenbyten’estpasautoriséeparaffectation.b=25;//2OK
D’unemanièregénérale,laconversiondeintenbyten’estpasacceptéeparaffectation.Mais une exception a lieu pour les expressions constantes (calculables à lacompilation),àconditionqueleurvaleursoitreprésentabledansletyped’arrivée,cequiestmanifestementlecasici.b=500;//3Erreur
Onestdanslamêmesituationqueprécédemment,aveccettedifférencequelavaleur500n’estpasreprésentabledansletypebyte.x=2*q;//4OK
Ici,l’expression2*qestévaluéeeneffectuantlaconversiond’ajustementdetypede2en long. Puis le résultat, de type long, est converti dans le type float avant d’êtreaffectéàx.y=b*b;//5OK
26
Lavaleurdel’expressionb*bestévaluéeeneffectuantlapromotionnumériquedebenint.Lerésultat,detypeint,estconvertidansletypedoubleavantd’êtreaffectéày.p=b*b;//6Erreur
Làencore,lavaleurdel’expressionb*bestdetypeint.Laconversiondeintenshortestillégaleparaffectation.b=b+5;//7Erreur
Lavaleurdel’expressionb+5estdetypeint.Laconversiondeintenshortestillégaleparaffectation.p=5*N-3;//8OK
L’expression5*N-3estde type int.Maiscomme il s’agitd’uneexpressionconstante(calculableàlacompilation),saconversionenshortestlégaleparaffectationpourpeuquesavaleursoitreprésentabledanscetype,cequiestlecasici.
27
8 Opérateursd’incrémentation,dedécrémentationetd’affectationélargie
Quelsrésultatsfournitceprogramme?publicclassOpIncr
{publicstaticvoidmain(String[]args)
{inti,j,n;
i=0;n=i++;
System.out.println("A:i="+i+"n="+n);
i=10;n=++i;
System.out.println("B:i="+i+"n="+n);
i=20;j=5;n=i++*++j;
System.out.println("C:i="+i+"j="+j+"n="+n
);
i=15;n=i+=3;
System.out.println("D:i="+i+"n="+n);
i=3;j=5;n=i*=--j;
System.out.println("E:i="+i+"j="+j+"n="+
n);
}
}
A:i=1n=0
B:i=11n=11
C:i=21j=6n=120
D:i=18n=18
E:i=12j=4n=12
28
9 Opérateursd’incrémentationetd’affectationélargie
Soitcesdéclarations:byteb;shortp;charc;intn;floatx;
Parmilesexpressionssuivantes,lesquellessontincorrectesetpourquoi?c=c+1//1
c++//2
c+=3//3
b+=c//4
p+=b//5
p=p+b//6
n+=x//7
n=n+x//8
x++;//9
c=c+1//1Erreur
L’expressionc+1estdutypeintquinepeutpasêtreconvertiencharparaffectation.c++//2OK
Ici,l’opérateur++appliquesonincrémentationde1directementàlavariablecdetypechar.Aucuneconversionn’estmiseenjeu.c+=3//3OK
Cetteexpressionestenfaitéquivalenteàc=(char)(c+3)etnonsimplementàc=c+3.Danscesconditions,elleévaluebien l’expressionc+3dans le type intmais elle enforceensuitelaconversionenchar.Notezbienquel’affectationc=c+3seraitillégale.b+=c//4OK
Cetteexpressionestéquivalenteàb=(byte)(b+c).p+=b//5OK
Cetteexpressionestéquivalenteàp=(short)(p+b).p=p+b//6Erreur
Lavaleurdel’expressionp+bestdetypeint(lesdeuxopérandesde+sontsoumisaux
29
promotionsnumériquesenint).Ellenepeutpasêtreconvertieenshortparaffectation.Notezladifférenceavecl’expressionprécédente.n+=x//7OK
Cette expression est équivalente à n = (int) (n+x). Notez cependant qu’on emploierarementl’opérateur+=decettemanière.n=n+x//8Erreur
L’expression n+x est de type float et sa valeur ne peut pas être convertie paraffectationenint.x++//9OK
Cetteexpressionjouelemêmerôlequex=x+1.Enpratique,onemploierarementlesopérateursd’incrémentationoudedécrémentationsurdesvariablesflottantes.
30
10 Opérateurconditionnel
Quelsrésultatsfournitceprogramme?publicclassOpCond
{publicstaticvoidmain(String[]args)
{intn=10,p=5,q=10;
n=p=q=5;
n+=p+=q;
System.out.println("A:n="+n+"p="+p+"q="+
q);
q=n<p?n++:p++;
System.out.println("B:n="+n+"p="+p+"q="+
q);
q=n>p?n++:p++;
System.out.println("C:n="+n+"p="+p+"q="+
q);
}
}
A:n=15p=10q=5
B:n=15p=11q=10
C:n=16p=11q=15
1.Entouterigueur,lavaleurdelavariablecestnonpas60,maisl’entierdontlecode(Unicode)estégalà60.2.Mêmeremarquequeprécédemment.
31
Chapitre2
Lesinstructionsdecontrôle
Connaissancesrequises
•Instructionssimples,instructionsstructurées,instructionscomposées(bloc)
•L’instructionif;casdesifimbriqués
•L’instructionswitch;l’étiquettedefault
•L’instructiondowhile
•L’instructionwhile
•L’instructionfor;initialisationavecéventuelledéclaration,conditiond’arrêt,incrémentation
•Lesinstructionsdebranchementinconditionnelbreaketcontinueavecousansétiquette
Note : on suppose qu’on dispose d’une classe nomméeClavier, comportant (entreautres) des méthodes (statiques) de lecture au clavier d’informations de type int(lireInt), float (lireFloat),double (lireDouble) et char (lireChar). Cette classe estprésentesurlesiteWebd’accompagnementetsalisteestfournieenAnnexeD.
32
11 Syntaxedeifetdeswitch
Quelleserreursontétécommisesdanschacundesgroupesd’instructionssuivants.On suppose que les variables concernées sont d’un type primitif numérique etqu’ellesontétécorrectementdéclarées(ungroupenecomporteaucuneerreur):
//groupe1
if(a<b)System.out.println("ascendant")
elseSystem.out.println("nonascendant");
//groupe2
if(a<b){System.out.println("ascendant);max=b}
//groupe3
intn,p;
.....
switch(n){case2:System.out.println("petit");break;
casep:System.out.println("limite");break;
}
//groupe4
intn;
finalintLIMITE=20;
.....
switch (n) { case LIMITE-1 : System.out.println ("un peu trop
petit");break;
caseLIMITE:System.out.println("OK");break;
case LIMITE+1 : System.out.println ("un peu trop
grand");break;
}
Groupe1Ilmanqueunpoint-virguleàlafindupremierappeldeSystem.out.println:if(a<b)System.out.println("ascendant");
elseSystem.out.println("nonascendant");
33
Groupe2Ilmanqueunpoint-virguleàlafindeladeuxièmeinstructiondubloc:if(a<b){System.out.println("ascendant);max=b;}
Groupe3Les valeurs utilisées dans les étiquettes de la forme case xxx doivent être desexpressionsconstantes,cequin’estpaslecasdep.
Groupe4Aucune erreur. Les expressions telles que LIMITE-1 étant bien cette fois desexpressionsconstantes.
34
12 Rôledel’instructionswitch
Soitleprogrammesuivanta:publicclassExoII2
{publicstaticvoidmain(String[]args)
{intn;
n=Clavier.lireInt();
switch(n)
{case0:System.out.println("Nul");
case1:
case2:System.out.println("Petit");
break;
case3:
case4:
case5:System.out.println("Moyen");
default:System.out.println("Grand");
}
}
}
Quelsrésultatsaffiche-t-illorsqu’onluifournitendonnée:1.lavaleur0,2.lavaleur1,3.lavaleur4,4.lavaleur10,5.lavaleur-5.
a.IlutiliselaclasseClavier(voirnoteendébutdechapitre).
//aveclavaleur0
Nul
Petit
//aveclavaleur1
35
Petit
//aveclavaleur4
Moyen
Grand
//aveclavaleur10
Grand
//aveclavaleur-5
Grand
36
13 Syntaxedesboucles
Quelleserreursontétécommisesdanschacunedesinstructionssuivantes?don++while(n<10);//instruction1
dowhile((n=Clavier.lireInt())!=10);//instruction2
do;while(true);//instruction3
do{}while(false);//instruction4
Instruction1Ilmanqueunpoint-virgule:don++;while(n<10);
Instruction2Ilmanqueuneinstruction(mêmevide)aprèslemotdo,parexemple:do;while((n=Clavier.lireInt())!=10);
ou:do{}while((n=Clavier.lireInt())!=10);
Instruction3Aucuneerreurdecompilationneseradétectée.Maisonestenprésenced’uneboucleinfinie.
Instruction4Aucuneerreurdecompilationneseradétectée.Maisl’instructionnesertàrien.
37
14 Comparaisonentrefor,whileetdo…while
Soitleprogrammesuivanta:publicclassExoII4a
{publicstaticvoidmain(String[]args)
{inti,n,som;
som=0;
for(i=0;i<4;i++)
{System.out.println("donnezunentier");
n=Clavier.lireInt();
som+=n;
}
System.out.println("Somme:"+som);
}
}
Écrire un programme réalisant la même chose en employant à la place del’instructionfor:1.uneinstructionwhile,2.uneinstructiondo…while.
a.IlutiliselaclasseClavier(voirnoteendébutdechapitre).
Avecuneinstructionwhile:publicclassExoII4b
{publicstaticvoidmain(String[]args)
{inti,n,som;
som=0;
i=0;
while(i<4)
{System.out.println("donnezunentier");
n=Clavier.lireInt();
38
som+=n;
i++;
}
System.out.println("Somme:"+som);
}
}
Avecuneinstructiondo…while:
publicclassExoII4c
{publicstaticvoidmain(String[]args)
{inti,n,som;
som=0;
i=0;
do
{System.out.println("donnezunentier");
n=Clavier.lireInt();
som+=n;
i++;
}
while(i<4);
System.out.println("Somme:"+som);
}
}
39
15 Rupturedeséquenceavecbreaketcontinue
Quelsrésultatsfournitleprogrammesuivant?publicclassExoII5
{publicstaticvoidmain(String[]args)
{intn=0;
do
{if(n%2==0){System.out.println(n+"estpair");
n+=3;
continue;
}
if(n%3==0){System.out.println(n+"estmultiplede3");
n+=5;
}
if(n%5==0){System.out.println(n+"estmultiplede5");
break;
}
n+=1;
}
while(true);
}
}
0estpair
3estmultiplede3
9estmultiplede3
15estmultiplede3
20estmultiplede5
40
16 Bouclewhile,opérateursd’affectationélargieetd’incrémentation(1)
Quelsrésultatsfournitleprogrammesuivant?publicclassExoII6
{publicstaticvoidmain(String[]args)
{intn,p;
n=0;
while(n<=5)n++;
System.out.println("A:n="+n);
n=p=0;
while(n<=8)n+=p++;
System.out.println("B:n="+n);
n=p=0;
while(n<=8)n+=++p;
System.out.println("C:n="+n);
n=p=0;
while(p<=5)n+=p++;
System.out.println("D:n="+n);
n=p=0;
while(p<=5)n+=++p;
System.out.println("D:n="+n);
}
}
A:n=6
B:n=10
41
C:n=10
D:n=15
D:n=21
42
17 Bouclewhile,opérateursd’affectationélargieetd’incrémentation(2)
Quelsrésultatsfournitleprogrammesuivant?publicclassExoII7
{publicstaticvoidmain(String[]args)
{intn,p;
n=p=0;
while(n<5)n+=2;p++;
System.out.println("A:n="+n+",p="+p);
n=p=0;
while(n<5){n+=2;p++;}
System.out.println("B:n="+n+",p="+p);
}
}
A:n=6,p=1
B:n=6,p=3
43
18 Syntaxegénéraledestroispartiesd’unebouclefor
Quelsrésultatsfournitleprogrammesuivant?publicclassExoII8
{publicstaticvoidmain(String[]args)
{inti,n;
for(i=0,n=0;i<5;i++)n++;
System.out.println("A:i="+i+",n="+n);
for(i=0,n=0;i<5;i++,n++){}
System.out.println("B:i="+i+",n="+n);
for(i=0,n=50;n>10;i++,n-=i){}
System.out.println("C:i="+i+",n="+n);
for(i=0,n=0;
i<3;i++,n+=i,System.out.println("D:i="+i+",n=
"+n));
System.out.println("E:i="+i+",n="+n);
}
}
A:i=5,n=5
B:i=5,n=5
C:i=9,n=5
D:i=1,n=1
D:i=2,n=3
D:i=3,n=6
E:i=3,n=6
44
19 Synthèse:calculd’unesuitederacinescarrées
Écrireunprogrammequicalculelesracinescarréesdenombresfournisendonnée.Ils’arrêteralorsqu’onluifourniralavaleur0a.Ilrefuseralesvaleursnégatives.Sonexécutionseprésenteraainsi:
donnezunnombrepositif:2
saracinecarreeest:1.4142135623730951
donnezunnombrepositif:-3
svppositif
donnezunnombrepositif:5
saracinecarreeest:2.23606797749979
donnezunnombrepositif:0
a.RappelonsquelaméthodeMath.sqrtfournitunrésultatdetypedoublecorrespondantàlavaleurdetypedoublefournieenargument.
Ilexistebeaucoupderédactionspossibles.Envoicitrois:
publicclassRacCara
{publicstaticvoidmain(String[]args)
{doublex;
do
{System.out.print("donnezunnombrepositif:");
x=Clavier.lireDouble();
if(x<0)System.out.println("svppositif");
if(x<=0)continue;
System.out.println("saracinecarreeest:"+Math.sqrt(x)
);
}
while(x!=0);
}
}
45
publicclassRacCarb
{publicstaticvoidmain(String[]args)
{doublex;
do
{System.out.print("donnezunnombrepositif:");
x=Clavier.lireDouble();
if(x<0){System.out.println("svppositif");
continue;
}
if (x>0) System.out.println ("sa racine carree est : " +
Math.sqrt(x));
}
while(x!=0);
}
}
publicclassRacCarc
{publicstaticvoidmain(String[]args)
{doublex;
do
{System.out.print("donnezunnombrepositif:");
x=Clavier.lireDouble();
if(x<0){System.out.println("svppositif");
continue;
}
if (x>0) System.out.println ("sa racine carree est : " +
Math.sqrt(x));
if(x==0)break;
}
while(true);
}
}
46
20 Synthèse:calculdelavaleurd’unesérie
Écrire un programme calculant la somme des n premiers termes de la "sérieharmonique",c’est-à-direlasomme:
1+1/2+1/3+1/4+.....+1/nLavaleurdenseralueendonnéea.
a.OnpourrautiliserlaclasseClavier(voirnoteendébutdechapitre).
publicclassSerie
{publicstaticvoidmain(String[]args)
{intnt;//nombredetermesdelaserieharmonique
floatsom;//pourlasommedelaserie
inti;
do
{System.out.print("combiendetermes:");
nt=Clavier.lireInt();
}
while(nt<1);
for(i=1,som=0;i<=nt;i++)som+=(float)1/i;
System.out.println ("Somme des " + nt + " premiers termes = " +
som);
}
}
1.Rappelonsquedans:som+=(float)1/i
l’opérateurfloatportesurl’entier1.Lepremieropérandedel’opérateur/estdoncdetype float ; par conséquent, son second opérande sera soumis à une promotion
47
numériqueenfloat,avantqu’onneprocèdeàladivision.Notezqu’ilfautéviterd’écrire:
som+=1/i
En effet dans ce cas l’opérateur / porterait sur deux entiers et correspondrait à ladivisionentière.Lerésultatseraittoujoursnul(saufpouri=1).Demême,enécrivant:
som+=(float)(1/i)
lerésultatneseraitpasplussatisfaisantpuisquelaconversionenflottantn’auraitlieuqu’aprèsladivision(enentier).Enrevanche,onpourraitécrire:
som+=1.0f/i
2.Onpeut améliorer la précision du résultat en effectuant la somme "à l’envers",c’est-àdireenallantdenvers1etnonpasde1versn.Ladifférencenedeviendracependantperceptiblequepourdegrandesvaleursden.
48
21 Synthèse:dessind’untriangleenmodetexte
Écrireunprogrammequiafficheuntriangleisocèleforméd’étoiles.Lahauteurdutriangle (c’est-à-dire son nombre de lignes) sera fourni en donnéea, comme dansl’exempleci-dessous.Ons’arrangerapourqueladernièrelignedutriangles’affichesurlebordgauchedel’écran.
combiendelignes?8
*
***
*****
*******
*********
***********
*************
***************
a.OnpourrautiliserlaclasseClavier(voirnoteendébutdechapitre).
publicclassDessin
{
publicstaticvoidmain(String[]args)
{
intnLignes;//nombretotaldelignes
intnumLigne;//compteurdeligne
int nEspaces ; // nombre d'espaces precedent une
etoile
final char cRempli = '*' ; // caractere de remplissage (ici
etoile)
intj;
System.out.print("combiendelignes?");
nLignes=Clavier.lireInt();
49
for(numLigne=0;numLigne<nLignes;numLigne++)
{nEspaces=nLignes-numLigne-1;
for(j=0;j<nEspaces;j++)System.out.print('');
for(j=0;j<2*numLigne+1;j++)System.out.print(cRempli);
System.out.println();
}
}
}
50
22 Synthèse:calculdecombinaisons
Écrire un programme qui affiche toutes lesmanières possibles d’obtenir un francavec des pièces de 2 centimes, 5 centimes et 10 centimes. Dire combien depossibilitésontainsiététrouvées.Lesrésultatsserontprésentésainsi:
1F=50X2c
1F=45X2c+2X5c
1F=40X2c+4X5c
1F=35X2c+6X5c
1F=30X2c+8X5c
1F=25X2c+10X5c
1F=20X2c+12X5c
1F=15X2c+14X5c
.....
1F=15X2c+7X10c
1F=10X2c+2X5c+7X10c
1F=5X2c+4X5c+7X10c
1F=6X5c+7X10c
1F=10X2c+8X10c
1F=5X2c+2X5c+8X10c
1F=4X5c+8X10c
1F=5X2c+9X10c
1F=2X5c+9X10c
1F=10X10c
Entout,ilya66faconsdefaire1F
publicclassCombis
{
publicstaticvoidmain(String[]args)
{
intnbf;/*compteurdunombredefaçonsdefaire1F*/
intn10;/*nombredepiècesde10centimes*/
intn5;/*nombredepiècesde5centimes*/
51
intn2;/*nombredepiècesde2centimes*/
nbf=0;
for(n10=0;n10<=10;n10++)
for(n5=0;n5<=20;n5++)
for(n2=0;n2<=50;n2++)
if(2*n2+5*n5+10*n10==100)
{nbf++;
System.out.print("1F=");
if(n2!=0)System.out.print(n2+"X2c");
if(n5!=0){if(n2!=0)System.out.print("+");
System.out.print(n5+"X5c");
}
if(n10!=0){if((n2!=0)||(n5!=0))System.out.print
("+");
System.out.print(n10+"10c");
}
System.out.println();
}
System.out.println("Entout,ilya"+nbf+"faconsdefaire1
F");
}
}
52
Chapitre3
Lesclassesetlesobjets
Connaissancesrequises
•Notiondeclasse:définitiondeschampsetdesméthodes,accèsprivésoupublicsauxmembres,utilisationd’uneclasse
•Miseenoeuvred’unprogrammecomportantplusieursclasses,àraisond’uneouplusieursclassesparfichiersource
•Notiondeconstructeur;règlesd’écritureetd’utilisation
•Lesdifférentesétapesdelacréationd’unobjet:initialisationpardéfaut,initialisationexplicite,appelduconstructeur;casparticulierdeschampsdéclarésavecl’attributfinal
•Affectationetcomparaisond’objets
•Notionderamasse-miettes
•Règlesd’écritured’uneméthode;méthodefonction,argumentsmuetsoueffectifs,règlesdeconversiondesargumentseffectifs,propriétésdesvariableslocales
•Champsetméthodesdeclasse;initialisationdeschampsdeclasse,blocd’initialisationstatique
•Surdéfinitiondeméthodes
•Lemotcléthis;casparticulierdel’appeld’unconstructeurauseindunautreconstructeur
•Récursivitédesméthodes
53
•Modedetransmissiondesargumentsetdelavaleurderetour
•Objetsmembres
•Paquetages
54
23 Créationetutilisationd’uneclassesimple
Réaliser une classePoint permettant de représenter un point sur un axe. Chaquepointseracaractériséparunnom(detypechar)etuneabscisse(detypedouble).Onprévoira:
•unconstructeurrecevantenargumentslenometl’abscissed’unpoint,
• une méthode affiche imprimant (en fenêtre console) le nom du point et sonabscisse,
• uneméthode translate effectuant une translation définie par la valeur de sonargument.
Écrireunpetitprogrammeutilisantcetteclassepourcréerunpoint,enafficherlescaractéristiques,ledéplaceretenafficherànouveaulescaractéristiques.
Ici,notreprogrammed’essai(méthodemain)estséparédelaclassePoint,maisplacédanslemêmefichiersource.LaclassePointnepeutdoncpasêtredéclaréepublique.Rappelonsque,danscesconditions,elleresteutilisabledepuisn’importequelleclassedupaquetagepardéfaut.
classPoint
{publicPoint(charc,doublex)//constructeur
{nom=c;
abs=x;
}
publicvoidaffiche()
{ System.out.println ("Point de nom " + nom + " d'abscisse " +
abs);
}
publicvoidtranslate(doubledx)
{abs+=dx;
}
55
privatecharnom;//nomdupoint
privatedoubleabs;//abscissedupoint
}
publicclassTstPtAxe
{publicstaticvoidmain(Stringargs[])
{Pointa=newPoint('C',2.5);
a.affiche();
Pointb=newPoint('D',5.25);
b.affiche();
b.translate(2.25);
b.affiche();
}
}
PointdenomCd'abscisse2.5
PointdenomDd'abscisse5.25
PointdenomDd'abscisse7.5
56
24 Initialisationd’unobjet
Quefournitleprogrammesuivant?classA
{publicA(intcoeff)
{nbre*=coeff;
nbre+=decal;
}
publicvoidaffiche()
{System.out.println("nbre="+nbre+"decal="+decal);
}
privateintnbre=20;
privateintdecal;
}
publicclassInitChmp
{publicstaticvoidmain(Stringargs[])
{Aa=newA(5);a.affiche();
}
}
Lacréationd’unobjetdetypeAentraînesuccessivement:
• l’initialisation par défaut de ses champs nbre et decal à une valeur "nulle" (icil’entier0),
•l’initialisationexplicitedeseschampslorsqu’elleexiste;icinbreprendlavaleur20,
• l’appel du constructeur : nbre est multiplié par la valeur de coeff (ici 5), puisincrémentédelavaleurdedecal(0).
Endéfinitive,leprogrammeaffiche:nbre=100decal=0
57
25 Champsconstants
Quelleerreuraétécommisedanscettedéfinitiondeclasse?classChCt
{publicChCt(floatr)
{x=r;
}
.....
privatefinalfloatx;
privatefinalintn=10;
privatefinalintp;
}
Le champp déclaré final doit être initialisé au plus tard par le constructeur, ce quin’est pas le cas. En revanche, les autres champs déclarés final sont correctementinitialisés,ndefaçonexpliciteetxparleconstructeur.
58
26 Affectationetcomparaisond’objets
Quefournitleprogrammesuivant?classEntier
{publicEntier(intnn){n=nn;}
publicvoidincr(intdn){n+=dn;}
publicvoidimprime(){System.out.println(n);}
privateintn;
}
publicclassTstEnt
{publicstaticvoidmain(Stringargs[])
{ Entier n1 = new Entier (2) ; System.out.print ("n1 = ") ;
n1.imprime();
Entier n2 = new Entier (5) ; System.out.print ("n1 = ") ;
n2.imprime();
n1.incr(3);System.out.print("n1=");n1.imprime();
System.out.println("n1==n2est"+(n1==n2));
n1 = n2 ; n2.incr(12) ; System.out.print ("n2 = ") ;
n2.imprime();
System.out.print("n1=");n1.imprime();
System.out.println("n1==n2est"+(n1==n2));
}
}
n1=2
n1=5
n1=5
n1==n2estfalse
n2=17
n1=17
n1==n2esttrue
L’opérateur==appliquéàdesobjetscompareleursréférences(etnonleursvaleurs).C’estpourquoilapremièrecomparaison(n1==n2)estfaussealorsquelesobjetsontla même valeur. La même reflexion s’applique à l’opérateur d’affectation. Après
59
exécutionden1=n2, les références contenues dans les variablesn1 etn2 sont lesmêmes. L’objet anciennement référencé par n2 n’étant plus référencé par ailleurs, ildevientcandidatauramasse-miettes.Dorénavantn1etn2référencentunseuletmêmeobjet.L’incrémentationdesavaleurparlebiaisden1seretrouveindifféremmentdansn1.imprimeetdansn2.imprime.Demême,lacomparaionn1==n2amaintenantlavaleurvrai.
60
27 Méthodesd’accèsauxchampsprivés
Soit leprogrammesuivant comportant ladéfinitiond’uneclassenomméePoint etsonutilisation:
classPoint
{publicPoint(intabs,intord){x=abs;y=ord;}
publicvoiddeplace(intdx,intdy){x+=dx;y+=dy;}
publicvoidaffiche()
{System.out.println("Jesuisunpointdecoordonnees"+x+
""+y);
}
privatedoublex;//abscisse
privatedoubley;//ordonnee
}
publicclassTstPnt
{publicstaticvoidmain(Stringargs[])
{Pointa;
a=newPoint(3,5);a.affiche();
a.deplace(2,0);a.affiche();
Pointb=newPoint(6,8);b.affiche();
}
}
Modifier la définition de la classePoint en supprimant laméthode affiche et enintroduisant deux méthodes d’accès nommées abscisse et ordonnee fournissantrespectivement l’abscisse et l’ordonnée d’un point. Adapter la méthodemain enconséquence.
classPoint
{publicPoint(intabs,intord){x=abs;y=ord;}
publicvoiddeplace(intdx,intdy){x+=dx;y+=dy;}
publicdoubleabscisse(){returnx;}
61
publicdoubleordonnee(){returny;}
privatedoublex;//abscisse
privatedoubley;//ordonnee
}
publicclassTstPnt1
{publicstaticvoidmain(Stringargs[])
{Pointa;
a=newPoint(3,5);
System.out.println("Jesuisunpointdecoordonnees"
+a.abscisse()+""+a.ordonnee());
a.deplace(2,0);
System.out.println("Jesuisunpointdecoordonnees"
+a.abscisse()+""+a.ordonnee());
Pointb=newPoint(6,8);
System.out.println("Jesuisunpointdecoordonnees"
+b.abscisse()+""+b.ordonnee());
}
}
Cet exemple était surtout destiné àmontrer que lesméthodes d’accès permettent derespecter l’encapsulation des données. Dans la pratique, la classe disposeraprobablementd’uneméthodeafficheenplusdesméthodesd’accès.
62
28 Conversionsd’arguments
Onsupposequ’ondisposedelaclasseAainsidéfinie:classA
{voidf(intn,floatx){.....}
voidg(byteb){.....}
.....
}
Soitcesdéclarations:Aa;intn;byteb;floatx;doubley;
Diresilesappelssuivantssontcorrectsetsinonpourquoi.a.f(n,x);
a.f(b+3,x);
a.f(b,x);
a.f(n,y);
a.f(n,(float)y);
a.f(n,2*x);
a.f(n+5,x+0.5);
a.g(b);
a.g(b+1);
a.g(b++);
a.g(3);
a.f(n,x);//OK:appelnormal
a.f(b+3,x);//OK:b+3estdéjàdetypeint
a.f(b,x);//OK:bdetypebyteseraconvertienint
a.f(n,y);//erreur:ydetypedoublenepeutêtre
convertienfloat
a.f(n,(float)y);//OK
a.f(n,2*x);//OK:2*xestdetypefloat
a.f(n+5,x+0.5);//erreur:0.5estdetypedouble,doncx+0.5
estde
// type double, lequel ne peut pas être converti en
63
float
a.g(b);//OK:appelnormal
a.g (b+1) ; // erreur : b1+1 de type int ne peut être
convertienbyte
a.g(b++);//OK:b1++estdetypeint
//(maispeuconseillé:onamodifiélavaleurdeb1)
a.g (3) ; // erreur : 3 de type int ne peut être
convertieenbyte
64
29 Champsetméthodesdeclasse(1)
Quelleserreursontétécommisesdansladéfinitiondeclassesuivanteetdanssonutilisation?
classA
{staticintf(intn)
{q=n;
}
voidg(intn)
{q=n;
p=n;
}
staticprivatefinalintp=20;
privateintq;
}
publicclassEssaiA
{publicstaticvoidmain(Stringargs[])
{Aa=newA();intn=5;
a.g(n);
a.f(n);
f(n);
}
}
Laméthodestatique fdeA nepeut pas agir sur un champnon statique ; l’affectationq=nestincorrecte.DanslaméthodegdeA,l’affectationq=nn’estpasusuellemaiselleestcorrecte.Enrevanche,l’affectationp=nnel’estpaspuisquepestfinal(ildoitdoncêtreinitialiséauplustardparleconstructeuretilnepeutplusêtremodifiéparlasuite).Danslaméthodemain,l’appela.f(n)seréfèreàunobjet,cequiestinutilemaistoléré.Il serait cependant préférable de l’écrireA.f(n).Quant à l’appel f(n) il est incorrectpuisqu’iln’existepasdeméthodefdanslaclasseEssaiA1. Ilestprobablequel’onavouluécrireA.f(n).
65
30 Champsetméthodesdeclasse(2)
Créeruneclassepermettantdemanipulerunpointd’unaxe,repéréparuneabscisse(detypeint).Ondevrapouvoireffectuerdeschangementsd’origine,enconservanten permanence l’abscisse d’une origine courante (initialement 0). On prévoirasimplementlesméthodessuivantes:
• constructeur, recevant en argument l’abscisse "absolue" du point (c’est-à-direrepéréeparrapportaupointd’origine0etnonparrapportàl’originecourante),
•affichequi imprimeà la fois l’abscissede l’originecouranteet l’abscissedupointparrapportàcetteorigine,
•setOriginequipermetdedéfinirunenouvelleabscissepourl’origine(expriméedefaçonabsolueetnonparrapportàl’originecourante),
•getOriginequipermetdeconnaîtrel’abscissedel’originecourante.Ecrireunpetitprogrammedetestfournissantlesrésultatssuivants:
Pointa-abscisse=3
relativeauneorigined'abscisse0
Pointb-abscisse=12
relativeauneorigined'abscisse0
Onplacel'origineen3
Pointa-abscisse=0
relativeauneorigined'abscisse3
Pointb-abscisse=9
relativeauneorigined'abscisse3
L’abscissedel’originecouranteestuneinformationquiconcernetouslespointsdelaclasse.Onenferadoncunchampdeclasseenledéclarantstatic.Delamêmemanière,les méthodes setOrigine et getOrigine concernent non pas un point donné, mais laclasse.Onenferadesméthodesdeclasseenlesdéclarantstatic.
classPoint
{publicPoint(intxx){x=xx;}
publicvoidaffiche()
66
{System.out.println("abscisse="+(x-origine));
System.out.println (" relative a une origine d'abscisse " +
origine);
}
publicstaticvoidsetOrigine(intorg){origine=org;}
publicstaticintgetOrigine(){returnorigine;}
private static int origine ; // abscisse absolue de l'origine
courante
privateintx;//abscisseabsoluedupoint
}
publicclassTstOrig
{publicstaticvoidmain(Stringargs[])
{ Point a = new Point (3) ; System.out.print ("Point a - ") ;
a.affiche();
Point b = new Point (12) ; System.out.print ("Point b - ") ;
b.affiche();
Point.setOrigine(3);
System.out.println ("On place l'origine en " +
Point.getOrigine());
System.out.print("Pointa-");a.affiche();
System.out.print("Pointb-");b.affiche();
}
}
67
31 Champsetméthodesdeclasse(3)
Réaliseruneclassequipermetd’attribuerunnumérouniqueàchaquenouvelobjetcréé (1aupremier,2au suivant…).Onnechercherapasà réutiliser lesnumérosd’objetséventuellementdétruits.Ondoteralaclasseuniquementd’unconstructeur,d’uneméthodegetIdent fournissant le numéro attribué à l’objet et d’uneméthodegetIdentMaxfournissantlenumérodudernierobjetcréé.Écrireunpetitprogrammed’essai.
Chaqueobjetdevradisposerd’unchamp(depréférenceprivé)destinéàconserversonnuméro. Par ailleurs, le constructeur d’un objet doit être en mesure de connaître ledernier numéro attribué. La démarche la plus naturelle consiste à le placer dans unchamp de classe (nommé ici numCour). La méthode getIdentMax est indépendanted’unquelconqueobjet;ilestpréférabled’enfaireuneméthodedeclasse.
classIdent
{publicIdent()
{numCour++;
num=numCour;
}
publicintgetIdent()
{returnnum;
}
publicstaticintgetIdentMax()
{returnnumCour;
}
privatestaticintnumCour=0;//derniernumeroattribué
privateintnum;//numerodel'objet
}
publicclassTstIdent
{publicstaticvoidmain(Stringargs[])
{Identa=newIdent(),b=newIdent();
System.out.println("numerodea:"+a.getIdent());
68
System.out.println("numerodeb:"+b.getIdent());
System.out.println("derniernumero"+Ident.getIdentMax());
Identc=newIdent();
System.out.println("derniernumero"+Ident.getIdentMax());
}
}
Ceprogrammefournitlesrésultatssuivants:numerodea:1
numerodeb:2
derniernumero2
derniernumero3
Sil’onsouhaitaitrécupérerlesidentificationsd’objetsdétruits,onpourraitexploiterlefaitqueJavaappellelaméthodefinalized’unobjetavantdelesoumettreauramasse-miettes. Il faudrait alors redéfinir cette méthode en conservant les numéros ainsirécupérés et en les réutilisant dans une construction ultérieure d’objet, ce quicompliqueraitquelquepeuladéfinitiondelaclasse.Deplus,ilnefaudraitpasperdredevuequ’unobjetn’estsoumisauramasse-miettesqu’encasdebesoindemémoireetnonpasnécessairementdèsqu’iln’estplusréférencé.
69
32 Blocd’initialisationstatique
Adapterlaclasseprécédente,demanièrequelenuméroinitialdesobjetssoitluauclaviera.Ondevras’assurerquelaréponsedel’utilisateureststrictementpositive.
a.OnpourrautiliserlaméthodelireIntdelaclasseClavierfourniesurlesiteWebd’accompagnementetdontlalistefigureenAnnexeD
S’il n’était pas nécessaire d’effectuer un test sur la valeur fournie au clavier, onpourraitsecontenterdemodifierainsilaclasseIdentprécédente:
publicIdent()
{num=numCour;
numCour++;
}
.....
private static int numCour=Clavier.lireInt() ; // dernier numero
attribué
Notezcependantquel’utilisateurneseraitpasinforméqueleprogrammeattendqu’ilfrappeauclavier.Maisici, l’initialisationdenumCourn’estplusréduiteàunesimpleexpression.Ellefait donc obligatoirement intervenir plusieurs instructions et il est nécessaire derecouriràunblocd’initialisationstatiqueenprocédantainsi:
classIdent
{publicIdent()
{num=numCour;
numCour++;
}
publicintgetIdent()
{returnnum;
}
publicstaticintgetIdentMax()
{returnnumCour-1;
70
}
privatestaticintnumCour;//prochainnumeroaattribuer
privateintnum;//numerodel'objet
static
{System.out.print("donnezlepremieridentificateur:");
donumCour=Clavier.lireInt();while(numCour<=0);
}
}
Àtitre indicatif, avec lemêmeprogramme (main) que dans l’exercice précédent, onobtientcesrésultats:
donnezlepremieridentificateur:12
numerodea:12
numerodeb:13
derniernumero13
derniernumero14
1.Les instructions d’un bloc d’initialisation statique ne concernent aucun objet enparticulier ;ellesnepeuventdoncaccéderqu’àdeschampsstatiques.Enoutre,etcontrairement à ce qui se produit pour les instructions desméthodes, ces champsdoivent avoir été déclarés avant d’être utilisés. Ici, il est donc nécessaire que ladéclarationduchampstatiquenumCourfigureavantleblocstatique(enpratique,onatendanceàplacercesblocsenfindedéfinitiondeclasse).2.Lesinstructionsd’unblocd’initialisationsontexécutéesavanttoutecréationd’unobjetdelaclasse.Mêmesinotreprogrammenecréaitaucunobjet,ildemanderaitàl’utilisateurdeluifounirunnuméro.
71
33 Surdéfinitiondeméthodes
Quelleserreursfigurentdansladéfinitiondeclassesuivante?classSurdef
{publicvoidf(intn){.....}
publicintf(intp){.....}
publicvoidg(floatx){.....}
publicvoidg(finaldoubley){.....}
publicvoidh(longn){.....}
publicinth(finallongp){.....}
}
Lesdeuxméthodesfontdesargumentsdemêmetype(lavaleurderetourn’intervenantpasdanslasurdéfinitiondesfonctions).Ilyadoncuneambiguïtéquiseradétectéedèslacompilationdelaclasse,indépendammentd’unequelconqueutilisation.Lasurdéfinitiondesméthodesgneprésentepasd’anomalie, leursargumentsétantdetypesdifférents.Enfin,lesdeuxméthodeshontdesargumentsdemêmetype(long),lequalificatiffinaln’intervenantpasici.Lacompilationsignaleraégalementuneambiguïtéàceniveau.
72
34 Recherched’uneméthodesurdéfinie(1)
Soitladéfinitiondeclassesuivante:classA
{publicvoidf(intn){.....}
publicvoidf(intn,intq){.....}
publicvoidf(intn,doubley){.....}
}
Aveccesdéclarations:Aa;byteb;shortp;intn;longq;floatx;doubley;
Quelles sont les instructions correctes et, dans ce cas, quelles sont lesméthodesappeléesetleséventuellesconversionsmisesenjeu?
a.f(n);
a.f(n,q);
a.f(q);
a.f(p,n);
a.f(b,x);
a.f(q,x);
a.f(n);//appelf(int)
a.f(n, q) ; // appel f(int, double) après conversion de q en
double
a.f(q);//erreur:aucuneméthodeacceptable
a.f(p,n);//appelf(int,int)aprèsconversiondepenint
a.f(b,x);//appelf(int,double)aprèsconversiondebenint
//etdexendouble
a.f(q,x);//erreur:aucuneméthodeacceptable
73
35 Recherched’uneméthodesurdéfinie(2)
Soitladéfinitiondeclassesuivante:classA
{publicvoidf(byteb){.....}
publicvoidf(intn){.....}
publicvoidf(floatx){.....}
publicvoidf(doubley){.....}
}
Aveccesdéclarations:Aa;byteb;shortp;intn;longq;floatx;doubley;
Quellessontlesméthodesappeléesetleséventuellesconversionsmisesenjeudanschacunedesinstructionssuivantes?
a.f(b);
a.f(p);
a.f(q);
a.f(x);
a.f(y);
a.f(2.*x);
a.f(b+1);
a.f(b++);
a.f(b);//appeldef(byte)
a.f(p);//appeldef(int)
a.f(q);//appeldef(float)aprèsconversiondeqenfloat
a.f(x);//appeldef(float)
a.f(y);//appeldef(double)
a.f(2.*x);//appeldef(double)car2.estdetypedouble;
//l'expression2.*xestdetypedouble
a.f(b+1);//appeldef(int)carl'expressionb+1estdetype
int
74
a.f(b++);//appeldef(byte)carl'expressionb++estdetype
byte
75
36 Recherched’uneméthodesurdéfinie(3)
Soitladéfinitiondeclassesuivante:classA
{publicvoidf(intn,floatx)
{.....}
publicvoidf(floatx1,floatx2)
{.....}
publicvoidf(floatx,intn)
{.....}
}
Aveccesdéclarations:Aa;shortp;intn1,n2;floatx;
Quelles sont les instructions correctes et, dans ce cas, quelles sont lesméthodesappeléesetleséventuellesconversionsmisesenjeu?
a.f(n1,x);
a.f(x,n1);
a.f(p,x);
a.f(n1,n2);
a.f(n1,x);
Lesméthodesf(int,float)etf(float,float)sontacceptablesmaislasecondeestmoinsbonnequelapremière.Ilyadoncappeldef(int,float).
a.f(x,n1);
Lesméthodesf(float,float)etf(float,int)sontacceptablesmaislapremièreestmoinsbonnequelaseconde.Ilyadoncappeldef(float,int).
a.f(p,x);
Lestroisméthodessontacceptables.Lasecondeetlatroisièmesontmoinsbonnesquelapremière.Ilyadoncappeldef(int,float)aprèsconversiondepenint.
a.f(n1,n2);
76
Lestroisméthodessontacceptables.Seulelasecondeestmoinsbonnequelesautres.Commeaucunedesdeuxméthodes f(int, float) et f(float, int) n'estmeilleure que lesautres,ilyaerreur.
77
37 Surdéfinitionetdroitsd’accès
Quelsrésultatsfournitceprogramme?classA
{publicvoidf(intn,floatx)
{System.out.println("f(intn,floatx)n="+n+"x="+
x);
}
privatevoidf(longq,doubley)
{System.out.println("f(longq,doubley)q="+q+"y="
+y);
}
publicvoidf(doubley1,doubley2)
{System.out.println("f(doubley1,doubley2)y1="+y1+"
y2="+y2);
}
publicvoidg()
{intn=1;longq=12;floatx=1.5f;doubley=2.5;
System.out.println("---dansg");
f(n,q);
f(q,n);
f(n,x);
f(n,y);
}
}
publicclassSurdfAcc
{publicstaticvoidmain(Stringargs[])
{Aa=newA();
a.g();
System.out.println("---dansmain");
intn=1;longq=12;floatx=1.5f;doubley=2.5;
a.f(n,q);
a.f(q,n);
a.f(n,x);
a.f(n,y);
}
78
}
---dansg
f(intn,floatx)n=1x=12.0
f(longq,doubley)q=12y=1.0
f(intn,floatx)n=1x=1.5
f(longq,doubley)q=1y=2.5
---dansmain
f(intn,floatx)n=1x=12.0
f(doubley1,doubley2)y1=12.0y2=1.0
f(intn,floatx)n=1x=1.5
f(doubley1,doubley2)y1=1.0y2=2.5
Laméthodef(long,double)étantprivée,ellen’estaccessiblequedepuislesméthodesdelaclasse.Ici,elleestdoncaccessibledepuisgetelleintervientdanslarecherchede la meilleure correspondance dans un appel de f. En revanche, elle ne l’est pasdepuismain. Ceci explique les différences constatées dans les deux séries d’appelsidentiques,l’unedepuisg,l’autredepuismain.
79
38 Emploidethis
SoitlaclassePointainsidéfinie:classPoint
{publicPoint(intabs,intord){x=abs;y=ord;}
publicvoidaffiche()
{System.out.println("Coordonnees"+x+""+y);
}
privatedoublex;//abscisse
privatedoubley;//ordonnee
}
LuiajouteruneméthodemaxNormedéterminantparmideuxpointslequelestlepluséloigné de l’origine et le fournissant en valeur de retour. On donnera deuxsolutions:
•maxNormeestuneméthodestatiquedePoint,
•maxNormeestuneméthodeusuelledePoint.
AvecuneméthodestatiqueLaméthodemaxNormevadevoirdisposerdedeuxargumentsdetypePoint.Ici,nousnouscontentonsdecalculerlecarrédelanormedusegmentjoignantl’origineaupointconcerné.Ilsuffitensuitedefournircommevaleurderetourceluidesdeuxpointspourlequelcettevaleurest laplusgrande.Voici lanouvelledéfinitiondelaclassePoint,accompagnéed’unprogrammedetestetdesrésultatsfournisparsonexécution:
classPoint
{publicPoint(intabs,intord){x=abs;y=ord;}
publicvoidaffiche()
{System.out.println("Coordonnees"+x+""+y);
}
publicstaticPointMaxNorme(Pointa,Pointb)
{doublena=a.x*a.x+a.y*a.y;
doublenb=b.x*b.x+b.y*b.y;
80
if(na>nb)returna;
elsereturnb;
}
privatedoublex;//abscisse
privatedoubley;//ordonnee
}
publicclassMaxNorme
{publicstaticvoidmain(Stringargs[])
{ Point p1 = new Point (2, 5) ; System.out.print ("p1 : ") ;
p1.affiche();
Point p2 = new Point (3, 1) ; System.out.print ("p2 : ") ;
p2.affiche();
Pointp=Point.MaxNorme(p1,p2);
System.out.print("Maxdep1etp2:");p.affiche();
}
}
p1:Coordonnees2.05.0
p2:Coordonnees3.01.0
Maxdep1etp2:Coordonnees2.05.0
AvecuneméthodeusuelleCettefois,laméthodenedisposeplusqued’unseulargumentdetypePoint,lesecondpoint concerné étant celui ayant appelé la méthode et dont la référence se notesimplementthis.Voici la nouvelle définition de la classe et l’adaptation du programme d’essai (quifournitlesmêmesrésultatsqueprécédemment):
classPoint
{publicPoint(intabs,intord){x=abs;y=ord;}
publicvoidaffiche()
{System.out.println("Coordonnees"+x+""+y);
}
publicPointMaxNorme(Pointb)
{ double na = x*x + y*y ; // ou encore this.x*this.x +
this.y*this.y
doublenb=b.x*b.x+b.y*b.y;
if(na>nb)returnthis;
elsereturnb;
}
privatedoublex;//abscisse
81
privatedoubley;//ordonnee
}
publicclassMaxNorm2
{publicstaticvoidmain(Stringargs[])
{ Point p1 = new Point (2, 5) ; System.out.print ("p1 : ") ;
p1.affiche();
Point p2 = new Point (3, 1) ; System.out.print ("p2 : ") ;
p2.affiche();
Pointp=p1.MaxNorme(p2);//oup2.maxNorme(p1)
System.out.print("Maxdep1etp2:");p.affiche();
}
}
82
39 Récursivitédesméthodes
Écrire une méthode statique d’une classe statiqueUtil calculant la valeur de la"fonctiond’Ackermann"Adéfiniepourm>=0etn>=0par:
•A(m,n)=A(m-1,A(m,n-1))pourm>0etn>0,
•A(0,n)=n+1pourn>0,
•A(m,0)=A(m-1,1)pourm>0.
Il suffit d’exploiter les possibilités de récursivité de Java en écrivant quasitextuellementlesdéfinitionsrécursivesdelafonctionA.
classUtil
{publicstaticintacker(intm,intn)
{if((m<0)||(n<0))return0;//protection:0siarguments
incorrects
elseif(m==0)returnn+1;
elseif(n==0)returnacker(m-1,1);
elsereturnacker(m-1,acker(m,n-1));
}
}
publicclassAcker
{publicstaticvoidmain(Stringargs[])
{intm,n;
System.out.print("Premierparametre:");
m=Clavier.lireInt();
System.out.print("Secondparametre:");
n=Clavier.lireInt();
System.out.println ("acker (" + m + ", " + n + ") = " +
Util.acker(m,n));
}
}
83
40 Modedetransmissiondesargumentsd’uneméthode
Quelsrésultatsfournitceprogramme?classA
{publicA(intnn)
{n=nn;
}
publicintgetn()
{returnn;
}
publicvoidsetn(intnn)
{n=nn;
}
privateintn;
}
classUtil
{publicstaticvoidincre(Aa,intp)
{a.setn(a.getn()+p);
}
publicstaticvoidincre(intn,intp)
{n+=p;
}
}
publicclassTrans
{publicstaticvoidmain(Stringargs[])
{Aa=newA(2);
intn=2;
System.out.println("valeurdeaavant:"+a.getn());
Util.incre(a,5);
System.out.println("valeurdeaapres:"+a.getn());
System.out.println("valeurdenavant:"+n);
Util.incre(n,5);
84
System.out.println("valeurdenapres:"+n);
}
}
EnJava,letransfertdesargumentsàuneméthodesefait toujoursparvaleur.Maislavaleurd’unevariabledetypeobjetestsaréférence.D’oùlesrésultats:
valeurdeaavant:2
valeurdeaapres:7
valeurdenavant:2
valeurdenapres:2
85
41 Objetsmembres
On dispose de la classePoint suivante permettant de manipuler des points d’unplan.classPoint
{publicPoint(doublex,doubley){this.x=x;this.y=y;}
publicvoiddeplace(doubledx,doubledy){x+=dx;y+=dy;
}
publicvoidaffiche()
{System.out.println("coordonnees="+x+""+y);
}
privatedoublex,y;
}
En ajoutant les fonctionnalités nécessaires à la classePoint, réaliser une classeSegmentpermettantdemanipulerdessegmentsd’unplanetdisposantdesméthodessuivantes:
segment(Pointorigine,Pointextremite)
segment(doublexOr,doubleyOr,doublexExt,doubleyExt)
doublelongueur();
voiddeplaceOrigine(doubledx,doubledy)
voiddeplaceExtremite(doubledx,doubledy)
voidaffiche()
Pourl’instant,laclassePointn’estdotéenideméthodesd’accèsauxchampsxety,nideméthodesd’altérationdeleursvaleurs.Si l’on prévoit de représenter un segment par deux objets de typePoint2, il faudramanifestementpouvoirconnaîtreetmodifierleurscoordonnéespourpouvoirdéplacerl’origineoul’extrémitédusegment.Pourcefaire,onpourraparexempleajouteràlaclassePointlesquatreméthodessuivantes:
publicdoublegetX()
{returnx;
}
86
publicdoublegetY()
{returny;
}
publicvoidsetX(doublex)
{this.x=x;
}
publicvoidsetY(doubley)
{this.y=y;
}
EncequiconcernelaméthodeaffichedeSegment,onpeutsecontenterdefaireappelàcelledePoint,pourpeuqu’onsecontentedelaformedumessagequ’ellefournit.VoicilanouvelledéfinitiondePointetcelledeSegment:
classPoint
{publicPoint(doublex,doubley){this.x=x;this.y=y;}
publicvoiddeplace(doubledx,doubledy){x+=dx;y+=dy;}
publicdoublegetX(){returnx;}
publicdoublegetY(){returny;}
publicvoidsetX(doublex){this.x=x;}
publicvoidsetY(doubley){this.y=y;}
publicvoidaffiche()
{System.out.println("coordonnees="+x+""+y);
}
privatedoublex,y;
}
classSegment
{publicSegment(Pointor,Pointext)
{this.or=or;this.ext=ext;
}
publicSegment(doublexOr,doubleyOr,doublexExt,doubleyExt)
{or=newPoint(xOr,yOr);
ext=newPoint(xExt,yExt);
}
publicdoublelongueur()
{doublexOr=or.getX(),yOr=or.getY();
doublexExt=ext.getX(),yExt=ext.getY();
return Math.sqrt ( (xExt-xOr)*(xExt-xOr) + (yExt-yOr)*(yExt-yOr)
);
87
}
publicvoiddeplaceOrigine(doubledx,doubledy)
{or.setX(or.getX()+dx);
or.setY(or.getY()+dy);
}
publicvoiddeplaceExtremite(doubledx,doubledy)
{ext.setX(ext.getX()+dx);
ext.setY(ext.getY()+dy);
}
publicvoidaffiche()
{System.out.print("Origine-");or.affiche();
System.out.print("Extremite-");ext.affiche();
}
privatePointor,ext;
}
Voiciunpetitprogrammedetest,accompagnédesonrésultat:
publicclassTstSeg
{publicstaticvoidmain(Stringargs[])
{Pointa=newPoint(1,3);
Pointb=newPoint(4,8);
a.affiche();b.affiche();
Segments1=newSegment(a,b);
s1.affiche();
s1.deplaceOrigine(2,5);
s1.affiche();
Segments2=newSegment(3,4,5,6);
s2.affiche();
System.out.println("longueur="+s2.longueur());
s2.deplaceExtremite(-2,-2);
s2.affiche();
}
}
coordonnees=1.03.0
coordonnees=4.08.0
Origine-coordonnees=1.03.0
Extremite-coordonnees=4.08.0
88
Origine-coordonnees=3.08.0
Extremite-coordonnees=4.08.0
Origine-coordonnees=3.04.0
Extremite-coordonnees=5.06.0
longueur=2.8284271247461903
Origine-coordonnees=3.04.0
Extremite-coordonnees=3.04.0
89
42 Synthèse:repèrescartésiensetpolaires
SoitlaclassePointainsidéfinie:classPoint
{publicPoint(doublex,doubley){this.x=x;this.y=y;
}
public void deplace (double dx, double dy) { x += dx ; y +=
dy;}
publicdoubleabscisse(){returnx;}
publicdoubleordonnee(){returny;}
privatedoublex;//abscisse
privatedoubley;//ordonnee
}
Lacompléterenladotantdesméthodessuivantes:
• homothetie qui multiplie les coordonnées par une valeur (de type double)fournieenargument,
•rotationquieffectueunerotationdontl’angleestfournienargument,
•rhoetthetaquifournissentlescoordonnéespolairesdupoint,
•afficheCartquiaffichelescoordonnéescartésiennesdupoint,
•affichePolquiaffichelescoordonnéespolairesdupoint.
Laméthodehomothetieneprésenteaucunedifficulté.Enrevanche,laméthoderotationnécessite une transformation intermédiaire des coordonnées cartésiennes du point encoordonnées polaires. De même, les méthode rho et theta doivent calculerrespectivement le rayon vecteur et l’angle d’un point à partir de ses coordonnéescartésiennes.Lecalculd’angleaétéréaliséparlaméthodeMath.atan2(quireçoitenargumentuneabscissexxetuneordonnéeyy)pluspratiquequeatan(àlaquelleilfaudraitfournirlequotientyy/xx) car elle évite d’avoir à s’assurer quexx n’est pas nulle et à adapter
90
danscecasl’angleobtenu.VoiciladéfinitiondenotreclassePoint:
classPoint
{publicPoint(doublex,doubley){this.x=x;this.y=y;}
publicvoiddeplace(doubledx,doubledy){x+=dx;y+=dy;}
publicdoubleabscisse(){returnx;}
publicdoubleordonnee(){returny;}
publicvoidhomothetie(doublecoef){x*=coef;y*=coef;}
publicvoidrotation(doubleth)
{doubler=Math.sqrt(x*x+y*y);
doublet=atan2(y,x);
t+=th;
x=r*Math.cos(t);
y=r=Math.sin(t);
}
publicdoublerho(){returnMath.sqrt(x*x+y*y);}
publicdoubletheta(){returnatan2(y,x);}
publicvoidafficheCart()
{System.out.println("Coordonneescartesiennes="+x+""+y
);
}
publicvoidaffichePol()
{System.out.println("Coordonneespolaires="+Math.sqrt(x*x+
y*y)
+""+atan2(y,x));
}
privatedoublex;//abscisse
privatedoubley;//ordonnee
}
Voici à titre indicatif un petit programme d’essai, accompagné du résultat de sonexécution:
publicclassPntPol
{publicstaticvoidmain(Stringargs[])
{Pointa;
a=newPoint(1,1);a.afficheCart();a.affichePol();
a.deplace(-1,-1);a.afficheCart();a.affichePol();
Pointb=newPoint(1,0);b.afficheCart();b.affichePol();
91
b.homothetie(2);b.afficheCart();b.affichePol();
b.rotation(Math.PI);b.afficheCart();b.affichePol();
}
}
Coordonneescartesiennes=1.01.0
Coordonneespolaires=1.41421356237309510.7853981633974483
Coordonneescartesiennes=0.00.0
Coordonneespolaires=0.00.0
Coordonneescartesiennes=1.00.0
Coordonneespolaires=1.00.0
Coordonneescartesiennes=2.00.0
Coordonneespolaires=2.00.0
Coordonneescartesiennes=-2.01.2246467991473532E-16
Coordonneespolaires=2.03.141592653589793
92
43 Synthèse:modificationdel’implémentationd’uneclasse
Modifier la classePoint réaliséedans l’exercice42,demanièreque lesdonnées(privées) soient maintenant les coordonnées polaires d’un point et non plus sescoordonnéescartésiennes.Onferaensortequele"contrat"initialdelaclassesoitrespecté en évitant de modifier les champs publics ou les en-têtes de méthodespubliques(l’utilisationdelaclassedevracontinueràsefairedelamêmemanière).
Leconstructeurreçoittoujoursenargumentlescoordonnéescartésiennesd’unpoint.Ildoitdoncopérerlestransformationsappropriées.Par ailleurs, la méthode deplace reçoit un déplacement exprimé en coordonnéescartésiennes.Ilfautdonctoutd’aborddéterminerlescoordonnéescartésiennesdupointaprèsdéplacement,avantderepasserencoordonnéespolaires.En revanche, les méthodes homothetie et rotation s’expriment maintenant trèssimplement.Voiciladéfinitiondenotrenouvelleclasse.
classPoint
{publicPoint(doublex,doubley)
{rho=Math.sqrt(x*x+y*y);
theta=Math.atan(y/x);
}
publicvoiddeplace(doubledx,doubledy)
{doublex=rho*Math.cos(theta)+dx;
doubley=rho*Math.sin(theta)+dy;
rho=Math.sqrt(x*x+y*y);
theta=antan2(y,x);
}
publicdoubleabscisse(){returnrho*Math.cos(theta);}
publicdoubleordonnee(){returnrho*Math.sin(theta);}
publicvoidhomothetie(doublecoef){rho*=coef;}
93
publicvoidrotation(doubleth)
{theta+=th;
}
publicdoublerho(){returnrho;}
publicdoubletheta(){returntheta;}
publicvoidafficheCart()
{ System.out.println ("Coordonnees cartesiennes = " +
rho*Math.cos(theta)
+""+rho*Math.sin(theta));
}
publicvoidaffichePol()
{ System.out.println ("Coordonnees polaires = " + rho + " " +
theta);
}
privatedoublerho;//rayonvecteur
privatedoubletheta;//anglepolaire
}
Àtitre indicatif,nouspouvons testernotreclasseavec lemêmeprogrammequedansl’exerciceprécédent.Ilfournitlesmêmesrésultats,auxincertitudesdecalculprès:
publicclassPntPol2
{publicstaticvoidmain(Stringargs[])
{Pointa;
a=newPoint(1,1);a.afficheCart();a.affichePol();
a.deplace(-1,-1);a.afficheCart();a.affichePol();
Pointb=newPoint(1,0);b.afficheCart();b.affichePol();
b.homothetie(2);b.afficheCart();b.affichePol();
b.rotation(Math.PI);b.afficheCart();b.affichePol();
}
}
Coordonneescartesiennes=1.00000000000000021.0
Coordonneespolaires=1.41421356237309510.7853981633974483
Coordonneescartesiennes=2.220446049250313E-160.0
Coordonneespolaires=2.220446049250313E-160.0
Coordonneescartesiennes=1.00.0
Coordonneespolaires=1.00.0
Coordonneescartesiennes=2.00.0
Coordonneespolaires=2.00.0
Coordonneescartesiennes=-2.02.4492127076447545E-16
94
Coordonneespolaires=2.03.141592653589793
95
44 Synthèse:vecteursàtroiscomposantes
Réaliser une classe Vecteur3d permettant de manipuler des vecteurs à troiscomposantes(detypedouble)etdisposant:
•d’unconstructeuràtroisarguments,
•d’uneméthoded’affichagedescoordonnéesduvecteur,souslaforme:<composante_1,composante_2,composante_3>
•d’uneméthodefournissantlanormed’unvecteur,
•d’uneméthode(statique)fournissantlasommededeuxvecteurs,
•d’uneméthode(nonstatique)fournissantleproduitscalairededeuxvecteurs.Écrireunpetitprogramme(main)utilisantcetteclasse.
classVecteur3d
{publicVecteur3d(doublex,doubley,doublez)
{this.x=x;this.y=y;this.z=z;
}
publicvoidaffiche()
{System.out.println("<"+x+","+y+","+z+">");
}
publicdoublenorme()
{return(Math.sqrt(x*x+y*y+z*z));
}
publicstaticVecteur3dsomme(Vecteur3dv,Vecteur3dw)
{Vecteur3ds=newVecteur3d(0,0,0);
s.x=v.x+w.x;s.y=v.y+w.y;s.z=v.z+w.z;
returns;
}
publicdoublepScal(Vecteur3dv)
{return(x*v.x+y*v.y+z*v.z);
96
}
privatedoublex,y,z;
}
publicclassTstV3d
{publicstaticvoidmain(Stringargs[])
{Vecteur3dv1=newVecteur3d(3,2,5);
Vecteur3dv2=newVecteur3d(1,2,3);
Vecteur3dv3;
System.out.print("v1=");v1.affiche();
System.out.print("v2=");v2.affiche();
v3=Vecteur3d.somme(v1,v2);
System.out.print("v1+v2=");v3.affiche();
System.out.println ("v1.v2 = " + v1.pScal(v2)) ; // ou
v2.pScal(v1)
}
}
v1=<3.0,2.0,5.0>
v2=<1.0,2.0,3.0>
v1+v2=<4.0,4.0,8.0>
v1.v2=22.0
1.Lecorpsdelaméthodesommepourraitêtreécritdefaçonplusconcise:returnnewVecteur3d(v.x+w.x,v.y+w.y,v.z+w.z);
2.Lesinstructionssuivantesdemain:v3=Vecteur3d.somme(v1,v2);
System.out.print("v1+v2=");v3.affiche();
pourraientêtreremplacéespar:System.out.print ("v1 + v2 = " ) ; (Vecteur3d.somme(v1,
v2)).affiche();
3. Si la méthode pScal avait été prévue statique, son utilisation deviendraitsymétrique. Par exemple, au lieu de v1.pScal(v2) ou v2.pScal(v1), on écriraitVecteur3d.pScal(v1,v2).
97
45 Synthèse:nombressexagésimaux
Onsouhaitedisposerd’uneclassepermettantd’effectuerdesconversions(danslesdeux sens) entre nombre sexagésimaux (durée exprimée en heures, minutes,secondes)etdesnombresdécimaux(duréeexpriméeenheuresdécimales).Pourcefaire,onréaliserauneclassepermettantdereprésenterunedurée.Ellecomportera:
• un constructeur recevant trois arguments de type int représentant une valeurssexagésimale(heures,minutes,secondes)qu’onsupposeranormalisée(secondesetminutesentre0et59).Aucunelimitationneporterasurlesheures;
•unconstructeurrecevantunargumentdetypedouble représentantuneduréeenheures;
• une méthode getDec fournissant la valeur en heures décimales associée àl’objet,
•desméthodesgetH,getMetgetS fournissant les troiscomposantesdunombresexagésimalassociéàl’objet.
Onproposeradeuxsolutions:1.Avecunchamp(privé)représentantlavaleurdécimale,2.Avecdeschamps(privés)représentantlavaleursexagésimale.
EnconservantlavaleurdécimaleLes deux constructeurs ne posent pas de problème particulier, le second devantsimplementcalculerladuréeenheurescorrespondantàunnombredonnéd’heures,deminutesetdesecondes.LesméthodesgetH,getMetgetSutilisentlemêmeprincipe:lenombred’heuresn’estriend’autrequelapartieentièredeladuréedécimale.Enlesoustrayant de cette durée décimale, on obtient un résidu d’au plus une heure qu’onconvertit enminutes en lemultipliant par 60. Sa partie entière fournit le nombre deminutesqui,soustraitdurésiduhorairefournitunrésidud’auplusuneminute…
classSexDec
{publicSexDec(doubledec)
{this.dec=dec;
98
}
publicSexDec(inth,intmn,ints)
{dec=h+mn/60.+s/3600.;
}
publicdoublegetDec()
{returndec;
}
publicintgetH()
{inth=(int)dec;returnh;
}
publicintgetM()
{inth=(int)dec;
intmn=(int)(60*(dec-h));
returnmn;
}
publicintgetS()
{inth=(int)dec;
doubleminDec=60*(dec-h);
intmn=(int)minDec;
intsec=(int)(60*(minDec-mn));
returnsec;
}
privatedoubledec;
}
Voiciunpetitprogrammedetest,accompagnédurésultatd’exécution:
publicclassTSexDec1
{publicstaticvoidmain(Stringargs[])
{SexDech1=newSexDec(4.51);
System.out.println("h1-decimal="+h1.getDec()
+"Sexa="+h1.getH()+""+h1.getM()+""+h1.getS());
SexDech2=newSexDec(2,32,15);
System.out.println("h2-decimal="+h2.getDec()
+"Sexa="+h2.getH()+""+h2.getM()+""+h2.getS());
}
h1-decimal=4.51Sexa=43035
h2-decimal=2.5375Sexa=23215
99
EnconservantlavaleursexagésimaleCette fois, le constructeur recevant une valeur en heures décimales doit opérer desconversionsanaloguesàcellesopéréesprécédemmentparlesméthodesd’accèsgetH,getMetgetS.Enrevanche,lesautresméthodessonttrèssimples.
classSexDec
{publicSexDec(doubledec)
{h=(int)dec;
intminDec=(int)(60*(dec-h));
mn=(int)minDec;
s=(int)(60*(minDec-mn));
}
publicSexDec(inth,intmn,ints)
{this.h=h;this.mn=mn;this.s=s;
}
publicdoublegetDec()
{return(3600*h+60*mn+s)/3600.;
}
publicintgetH()
{returnh;
}
publicintgetM()
{returnmn;
}
publicintgetS()
{returns;
}
privateinth,mn,s;
}
Voicilemêmeprogrammedetestqueprécédemment,accompagnédesonexécution:
publicclassTSexDec2
{publicstaticvoidmain(Stringargs[])
{SexDech1=newSexDec(4.51);
System.out.println("h1-decimal="+h1.getDec()
+"Sexa="+h1.getH()+""+h1.getM()+""+h1.getS());
SexDech2=newSexDec(2,32,15);
System.out.println("h2-decimal="+h2.getDec()
+"Sexa="+h2.getH()+""+h2.getM()+""+h2.getS());
100
}
}
h1-decimal=4.5Sexa=4300
h2-decimal=2.5375Sexa=23215
Onnoteraquelapremièredémarchepermetdeconserveruneduréedécimaleatteignantlaprécisiondutypedouble,quitteàcequelavaleursexagésimalecorrespondantesoitarrondieàlasecondelaplusproche.Ladeuxièmedémarche,enrevanche,enimposantd’embléeunnombreentierdesecondes,entraîneuneerreurd’arrondidéfinitive(entre0et1seconde)dèslacréationdel’objet.Bienentendu,onpourraitréglerleproblèmeen conservant un nombre de secondes décimal ou encore, en gérant un résidu desecondes.
1.SilaméthodemainavaitétéintroduitedirectementdansA,l’appelseraitaccepté!2.Onpourraitsecontenterd’ajouterdesméthodesgetXetgetY,enreprésentantunsegment,nonpluspardeuxpoints,maisparquatrevaleursdetypedouble,cequiseraitmoinscommode.
101
Chapitre4
Lestableaux
Connaissancesrequises
•Déclarationd’untableau;utilisationéventuelled’uninitialiseur
•Créationd’untableauavecl’opérateurnew
•Accèsauxélémentsd’untableau
•Affectationdetableaux
•Lechamplength
•Transmissiondetableauxenargumentd’uneméthode
•Tableauxdetableaux;leurutilisationpour"simuler"lestableauxàplusieursindices
102
46 Déclarationetinitialisationdetableau
Quelleserreursontétécommisesdansledébutdeprogrammesuivant?publicstaticvoidmain(Stringargs[])
{intn=10;
finalintp=5;
intt1[]={1,3,5};
intt2[]={n-1,n,n+1};
intt3[]={p-1,p,p+1};
intt4[];
t4={1,3,5};
floatx1[]={1,2,p,p+1};
floatx2[]={1.25,2.5,5};
doublex3[]={1,2.5,5.25,2*p};
.....
intt1[]={1,3,5};//OK
intt2[]={n-1,n,n+1};//OK
intt3[]={p-1,p,p+1};//OK
Notez que les expressions utilisées dans un initialiseur de tableau n’ont pas besoind’être des expressions constantes. Il suffit qu’elles soient calculables aumoment oùl’onexécuteladéclarationcorrespondante,cequiestlecasici.intt4[];
t4={1,3,5};//erreur
Lanotation{…}n’estutilisablequedans ladéclarationd’untableau.Ici, il fautsoitdéclarer:intt4={1,3,5};
soitaffecterdesvaleursàchacundesélémentsdet4,aprèssadéclaration.floatx1[]={1,2,p,p+1};//OK
Iln’estpasobligatoirequelesvaleursfigurantdansuninitialiseurdetableausoientdu
103
typedesélémentsdutableau,maisseulementd’untypecompatibleparaffectation,cequiestlecasici.floatx2[]={1.25,2.5,5};//erreur
Ici,enrevanche,lesconstantes1.25et2.5sontd’untypedouble,noncompatibleparaffectationavecletypefloatdutableau.doublex3[]={1,2.5,5.25,2*p};//OK
Ici, toutes les valeurs de l’initialiseur sont compatibles par affectation avec le typedouble.
104
47 Utilisationusuelled’untableau(1)
Écrireunprogrammequi créeun tableaucomportant lesvaleursdescarrésdesnpremiersnombresimpairs,lavaleurdenétantlueauclavieraetquienaffichelesvaleurssouslaformesuivante:
combiendevaleurs:5
1apourcarre1
3apourcarre9
5apourcarre25
7apourcarre49
9apourcarre81
a.OnpourrautiliserlaméthodelireIntdelaclasseClavierfourniesurlesiteWebd’accompagnement.
EnJava,latailled’untableaun’estdéfiniequ’aumomentdesacréation,cequinouspermeticidelalireauclavier:
publicclassCarrImp
{publicstaticvoidmain(Stringargs[])
{intcar[];
intn;
System.out.print("combiendevaleurs:");
n=Clavier.lireInt();
car=newint[n];
for (int i=0 ; i<n ; i++) // ici, for… each n’est pas
applicable
car[i]=(2*i+1)*(2*i+1);
for(inti=0;i<n;i++)//icinonplus(onabesoinde
i)
System.out.println((2*i+1)+"apourcarre"+car[i]);
}
}
105
Sil’énoncénel’avaitpasimposé,ilauraitétépossibledesepasserd’untableau.
106
48 Utilisationusuelled’untableau(2)
Écrireunprogrammequi:
•litdansuntableau5valeursflottantesfourniesauclaviera,
•encalculeetenaffichelamoyenne,laplusgrandeetlapluspetitevaleur.
a.OnpourrautiliserlaméthodelireIntdelaclasseClavierfourniesurlesiteWebd’accompagnement.
publicclassUtilTab1
{publicstaticvoidmain(Stringargs[])
{finalintN=5;
doubleval[]=newdouble[N];
inti;
System.out.println("donnez"+N+"valeursflottantes");
for (i=0 ; i<N ; i++) // for… each n’est pas
applicableici
val[i]=Clavier.lireDouble();
doublevalMax=val[0],valMin=val[0],somme=0;
for(i=0;i<N;i++)//ou(depuisJDK
5.0):
{if(val[i]>valMax)valMax=val[i];//for(doublev:val)
if (val[i] < valMin) valMin = val[i] ; // { if (v>valMax)
valMax=v;
somme += val[i] ; // if (v<valMin)
valMin=v;
}//som+=v;
//}
System.out.println("valeurmaximale="+valMax);
System.out.println("valeurminimale="+valMin);
doublevMoyenne=somme/N;//onsupposequeNeststrictement
positif
System.out.println("moyenne"+vMoyenne);
}
107
}
Iciencore,si l’énoncéne l’avaitpas imposé, ilauraitétépossibledesepasserd’untableau.
108
49 Affectationdetableaux(1)
Quesepassera-t-ilsil’onexécuteleprogrammesuivant?publicclassAffec1
{publicstaticvoidmain(Stringargs[])
{intt1[]={1,2,3};
intt2[]=newint[4];
for(inti=0;i<4;i++)t2[i]=2*i;
t2=t1;
for(inti=0;i<4;i++)System.out.println(t2[i]);
}
}
Ceprogramme crée tout d’abord deux tableaux d’entiers de dimension 3 et 4.Leursréférencesfigurentrespectivementdanslesvariablest1ett2.Aprèsl’instructionfor,lasituationseprésentecommeci-après:
Après l’affectation t2=t1, les deux variables t1 et t2 contiennent dorénavant laréférenceaupremiertableau,tandisquelesecondn’estplusréférencé(ilseracandidatauramasse-miettes).Lasituationestlasuivante:
109
Danscesconditions,ladernièreboucleafficheratoutd’abordlesvaleurs1,2et3puisprovoquera une erreur d’exécution pour i=3 (exceptionArrayIndexOutOfBoundsException), étant donné que l’on cherche à accéder à unélémentn’appartenantpasautableauconcerné.
110
50 Affectationdetableaux(2)
Quelsrésultatsfournitleprogrammesuivant?publicclassAffec
{publicstaticvoidmain(Stringargs[])
{finalintN=4;
intt1[]=newint[N];
intt2[]=newint[N];
for(inti=0;i<N;i++)t1[i]=i+1;
for(inti=0;i<N;i++)t2[i]=2*i+1;
//affichagedesvaleursdet1etdet2
System.out.print("t1=");
for(inti=0;i<N;i++)System.out.print(t1[i]+"");
System.out.println();
System.out.print("t2=");
for(inti=0;i<N;i++)System.out.print(t2[i]+"");
System.out.println();
t1=t2;
t1[0]=10;t2[1]=20;t1[2]=30;t2[3]=40;
//affichagedesvaleursdet1etdet2
System.out.print("t1=");
for(inti=0;i<N;i++)System.out.print(t1[i]+"");
System.out.println();
System.out.print("t2=");
for(inti=0;i<N;i++)System.out.print(t2[i]+"");
System.out.println();
}
}
Ceprogrammecrée toutd’aborddeuxtableauxenplaçant leursréférencesdans t1ett2.Mais après l’affectation t1=t2, t1 et t2 contiennent la même référence (celle dupremiertableau,leseconddevenantcandidatauramasse-miettes).Danscesconditions,
111
une instruction telle que t2[1]=20 a le même effet que t1[1]=20. En définitive, leprogrammefournitlesrésultatssuivants:
t1=1234
t2=1357
t1=10203040
t2=10203040
112
51 Affectationdetableaux(3)
Quelsrésultatsfournitleprogrammesuivant?publicclassAffec2
{publicstaticvoidmain(Stringargs[])
{chart1[]={'b','o','n','j','o','u','r'};
chart2[]={'h','e','l','l','o'};
chart3[]={'x','x','x','x'};
t3=t1;t1=t2;t2=t3;
System.out.print("t1=");
for(inti=0;i<t1.length;i++)System.out.print(t1[i]);
System.out.println();
System.out.print("t2=");
for(inti=0;i<t2.length;i++)System.out.print(t2[i]);
System.out.println();
System.out.print("t3=");
for(inti=0;i<t3.length;i++)System.out.print(t3[i]);
System.out.println();
}
}
Ceprogrammecréetroistableauxdecaractères,lesinitialiseetplaceleursréférencesrespectivesdanst1,t2ett3.Aprèsexécutiondestroisaffectations,letroisièmetableaun’est plus référencé, tandis que le premier l’est deux fois (par t2 et par t3). Endéfinitive,nousobtenonslesrésultatssuivants:
t1=hello
t2=bonjour
t3=bonjour
113
52 Tableauenargument(1)
Récrireleprogrammedel’exercicenuméro50,enprévoyantuneméthodestatiquedestinéeàafficherlesvaleursd’untableaureçuenargument.
Laméthoded’affichagedoitrecevoirenargumentlaréférenceàuntableaud’entiers.Iln’estpasnécessairedeprévoirunargumentsupplémentairepourlenombred’élémentsdutableau;celui-cipourraêtreobtenuàl’aideduchamplength.Ici,nousplaçonslaméthoded’affichagenomméeaffichedans lamêmeclasseque laméthodemain,cequinousconduitauprogrammesuivant:
publicclassTabArg1
{publicstaticvoidmain(Stringargs[])
{finalintN=4;
intt1[]=newint[N];
intt2[]=newint[N];
for(inti=0;i<N;i++)t1[i]=i+1;
for(inti=0;i<N;i++)t2[i]=2*i+1;
//affichagedesvaleursdet1etdet2
System.out.print("t1=");affiche(t1);
System.out.print("t2=");affiche(t2);
t1=t2;
t1[0]=10;t2[1]=20;t1[2]=30;t2[3]=40;
//affichagedesvaleursdet1etdet2
System.out.print("t1=");affiche(t1);
System.out.print("t2=");affiche(t2);
}
staticvoidaffiche(int[]t)//ou(depuisJDK5.0)
{for(inti=0;i<t.length;i++)//for(intv:t)
System.out.print(t[i]+"");//System.out.print(v+"");
System.out.println();
114
}
}
Ici, comme laméthodeaffiche figuredans laclasseTabArg1, il n’est pas nécessaire(bienquecelanesoitpasinterdit)d’enpréfixerlesappelsparTabArg1enécrivantparexemple TabArg1.affiche (t1). En revanche, cela deviendrait indispensable si laméthodeaffichefiguraitdansuneautreclassequecelleoùelleestutilisée.
115
53 Tableauenargument(2)
ÉcrireuneclasseutilitaireUtilTabdisposantdesméthodesstatiquessuivantes:
•sommequifournitlasommedesvaleursd’untableauderéels(double)detaillequelconque,
• incre qui incrémente d’une valeur donnée toutes les valeurs d’un tableau deréels(double).
Écrire unpetit programmed’essai. Pour faciliter les choses, on pourra égalementdoterlaclasseUtilTabd’uneméthoded’affichagedesvaleursd’untableauderéels.
Pourréaliserlaméthodeincre,onexploitelefaitquelorsqu’untableauesttransmisenargumentd’uneméthode,celle-cireçoitunecopiedelaréférencecorrespondante,parlebiaisdelaquelleellepeutmodifierlesvaleursdutableau.Onretrouvelàlemêmemécanisme que pour les objets. L’écriture des autres méthodes ne pose pas deproblèmeparticulier.
classUtilTab
{staticdoublesomme(double[]t)
{doubles=0.;//ou(depuisJDK
5.0):
for(inti=0;i<t.length;i++)s+=t[i];//for(intv:t)
s+=v;
returns;
}
staticvoidincre(double[]t,doublea)
{for(inti=0;i<t.length;i++)t[i]+=a;//for…eachn’est
pas
}//applicable
staticvoidaffiche(double[]t)
{for(inti=0;i<t.length;i++)System.out.print(t[i]+"");
System.out.println();
}
116
}
publicclassTstUtil1
{publicstaticvoidmain(Stringargs[])
{doublet1[]={1.25,2.5,3.5,5.};
System.out.print("t1initial=");UtilTab.affiche(t1);
System.out.println("somme="+UtilTab.somme(t1));
UtilTab.incre(t1,1.25);
System.out.print("t1incremente=");UtilTab.affiche(t1);
System.out.println("somme="+UtilTab.somme(t1));
}
}
t1initial=1.252.53.55.0
somme=12.25
t1incremente=2.53.754.756.25
somme=17.25
117
54 Tableauenvaleurderetour
ÉcrireuneclasseutilitaireUtilTabdisposantdesméthodesstatiquessuivantes:
• genere qui fournit en retour un tableau des n premiers nombres impairs, lavaleurdenétantfournieenargument
• somme qui reçoit en argument deux vecteurs d’entiers de même taille et quifournitenretouruntableaureprésentantlasommedecesdeuxvecteurs.
Écrire unpetit programmed’essai. Pour faciliter les choses, on pourra égalementdoterlaclasseUtilTabd’uneméthoded’affichagedesvaleursd’untableauderéels.
Les méthodes de la classe UtilTab recevront tout naturellement en argument laréférenceàunoudeuxtableaux.Encequiconcerneleurrésultat(tableau),celui-ciseracrééetrempliauseindelaméthodequisecontenterad’enrenvoyerlaréférence.
classUtilTab
{publicstaticint[]genere(intn)
{int[]res=newint[n];
for(inti=0,j=1;i<n;i++,j+=2)res[i]=j;
returnres;
}
publicstaticint[]somme(intt1[],intt2[])
{intn=t1.length;
if(n!=t2.length)returnnull;
intres[]=newint[n];
for(inti=0;i<n;i++)res[i]=t1[i]+t2[i];
returnres;
}
publicstaticvoidaffiche(int[]t)
{for(inti=0;i<t.length;i++)
System.out.print(t[i]+"");
System.out.println();
118
}
}
publicclassTabValR
{publicstaticvoidmain(Stringargs[])
{intta[]={1,5,9};
System.out.print("ta=");UtilTab.affiche(ta);
inttb[]=UtilTab.genere(3);
System.out.print("tb=");UtilTab.affiche(tb);
inttc[]=UtilTab.somme(ta,tb);
System.out.print("tc=");UtilTab.affiche(tc);
}
}
ta=159
tb=135
tc=2814
Ilnefautpasperdredevuequ’enJava,lesemplacementsallouésàdesobjetsouàdestableauxnesont libérésque lorsqu’ilsnesontplus référencés.C’estcequipermetàuneméthodede renvoyer la référenceàunemplacementqu’elleaelle-mêmecréé. Iln’envapasdemêmedansun langagecommeC++quigèrede telsemplacementsdemanière"automatique",enleslibérantdèslasortiedelaméthode.
119
55 Tableauxdetableaux
Quelsrésultatsfournitleprogrammesuivant?publicclassTab2Ind1
{publicstaticvoidmain(Stringargs[])
{int[][]t=newint[3][];
for(inti=0;i<3;i++)
{t[i]=newint[i+1];
for(intj=0;j<t[i].length;j++)
t[i][j]=i+j;
}
for(inti=0;i<3;i++)
{System.out.print("tableaunumero"+i+"=");
for(intj=0;j<t[i].length;j++)
System.out.print(t[i][j]+"");
System.out.println();
}
}
}
L’instruction:int[][]t=newint[3][];
créeuntableaudetroisréférencesàdestableauxd’entiersetplacesaréférencedanst.Pourl’instant,lesréférencesauxtableauxd’entierssontinitialiséesàlavaleurnull.Pourchaquevaleurdei:
•l’instruction:t[i]=newint[i+1];
créeuntableaud’entiersdetaillei+1etenplacelaréférencedanst[i].
•l’instruction:t[i][j]=i+j;
placedesvaleursdanschacundesi+1élémentsdecetableau.
120
Endéfinitive,lasituationpeutêtreschématiséecommeci-après:
D’oùlesrésultats:
tableaunumero0=0
tableaunumero1=12
tableaunumero2=234
121
56 Synthèse:nombresaléatoiresethistogramme
Réaliser une classe nommée Aleat permettant de disposer de suites de nombresentiersaléatoires.Onyprévoiralesméthodessuivantes
• constructeur Aleat (int n, int lim), n représentant le nombre de valeurssouhaitées,appartenantàl’intervalle[0,lim],
•getValeur(intn)quifournitlavaleurderangndelasuite,
•getValeurs()quifournituntableaucontenanttouteslesvaleursdelasuite,
•histo()quifournitunhistogrammedesvaleursdelasuite,c’est-à-direuntableaudelim+1valeursdanslequelunélémentderangireprésentelenombredefoisoùlavaleuriestprésentedanslasuite.
Écrireunpetitprogrammed’utilisation.
Ici,lesvaleursaléatoiresserontdéterminéesparleconstructeuretconservéesdansuntableauprivénomméval.LaméthodeMath.random fournitunnombrealéatoire réeldans l’intervalle [0,1[. Ilfautdonclemultiplierparlim+1etenprendrelapartieentièrepourobtenirunentierappartenantàl’intervalle[0,lim].DansgetValeurs, nous évitonsde renvoyerdirectement la référence au tableauprivéval car sinon la méthode appelante pourrait en modifier la valeur. En fait, nousrenvoyons la référence à une copie du tableau (copie qui, quant à elle, restemodifiable!).Enfin,danshisto,noussommesamenésàcréerunnouveautableaupourycalculerl’histogramme.
classAleat
{publicAleat(intn,intl)
{nVal=n;limite=l;
val=newint[n];
for(inti=0;i<nVal;i++)
122
val[i]=(int)((l+1)*Math.random());
}
publicintgetValeur(intnum)
{returnval[num];}
publicint[]getValeurs()
{int[]res=newint[nVal];
for(inti=0;i<nVal;i++)
res[i]=val[i];
returnres;
}
publicint[]histo()
{int[]res=newint[limite+1];//pourallerde0àlimite
for(inti=0;i<nVal;i++)res[val[i]]++;
returnres;
}
privateint[]val;
privateintnVal,limite;
}
publicclassTstAleat
{publicstaticvoidmain(Stringargs[])
{finalintNS1=8,MAX1=5,NS2=10000,MAX2=9;
Aleatsuite1=newAleat(NS1,10);
System.out.print("suite1,valeurparvaleur=");
for(inti=0;i<NS1;i++)
System.out.print(suite1.getValeur(i)+"");
System.out.println();
System.out.print("suite1,globale=");
int[]valeurs=suite1.getValeurs();
for(inti=0;i<NS1;i++)
System.out.print(valeurs[i]+"");
System.out.println();
int[]hist=suite1.histo();
System.out.print("histogrammedesuite1=");
for(inti=0;i<=MAX1;i++)System.out.print(hist[i]+"");
System.out.println();
Aleatsuite2=newAleat(NS2,MAX2);
hist=suite2.histo();
System.out.print("histogrammedesuite2=");
for(inti=0;i<=MAX2;i++)System.out.print(hist[i]+"");
123
}
}
suite1,valeurparvaleur=3794107101
suite1,globale=3794107101
histogrammedesuite1=010110
histogramme de suite2 = 1057 1008 1010 1012 1050 940 976 963 963
1021
124
57 Synthèse:calculvectoriel
RéaliseruneclasseVecteurpermettantdemanipulerdesvecteursayantunnombrequelconquedecomposantesdetypedouble.Onyprévoira:
• un constructeurVecteur (int n), n représentant le nombre de composantes quiserontalorsinitialiséesàzéro,
• un constructeur Vecteur (int n, double x), n représentant le nombre decomposantesquiserontalorstoutesinitialiséesàlavaleurx,
• un constructeurVecteur (double [ ] v) qui créera un vecteur par recopie dutableauv,
• uneméthode (non statique)prod_scal fournissant le produit scalaire de deuxvecteurs(ici,silesdeuxvecteursnesontpasdemêmetaille,onsecontenteradefournirlavaleurzéro),
• une méthode (statique) somme fournissant la somme de deux vecteurs ; s’ilsn’ontpaslamêmetaille,onrenverrauneréférence"nulle",
•uneméthodeafficheaffichantlescomposantesd’unvecteur.Écrireunpetitprogrammed’utilisation.
Nousexploitonslapossibilitéd’appelerunconstructeurauseind’unautre;rappelonsquecetappeldoitobligatoirementêtrelapremièreinstructionduconstructeur.LaméthodesommedoitcréerunnouvelobjetdetypeVecteurpouryplacerlasommedesdeuxvecteursreçusenargument.
classVecteur
{publicVecteur(intn)
{this(n,0.);
}
publicVecteur(intn,doublex)
{vect=newdouble[n];
for(inti=0;i<n;i++)vect[i]=x;
}
125
publicVecteur(double[]v)
{intn=v.length;
vect=newdouble[n];
for(inti=0;i<n;i++)vect[i]=v[i];
}
publicdoubleprodScal(Vecteurw)
{if(vect.length!=w.vect.length)return0.;
doubleps=0.;
for(inti=0;i<vect.length;i++)
ps+=vect[i]*w.vect[i];
returnps;
}
publicstaticVecteursomme(Vecteurv1,Vecteurv2)
{if(v1.vect.length!=v2.vect.length)returnnull;
intn=v1.vect.length;
Vecteurres=newVecteur(n);
for(inti=0;i<n;i++)
res.vect[i]=v1.vect[i]+v2.vect[i];
returnres;
}
publicvoidaffiche()
{for(inti=0;i<vect.length;i++)
System.out.print(vect[i]+"");
System.out.println();
}
privatedouble[]vect;
}
publicclassTstVect
{publicstaticvoidmain(Stringargs[])
{Vecteura=newVecteur(5);a.affiche();
Vecteurb=newVecteur(5,0.5);b.affiche();
System.out.println("a.b="+a.prodScal(b));
double[]valeurs={1.25,2.5,5.25,3,1};
Vecteurc=newVecteur(valeurs);c.affiche();
System.out.println("b.c="+b.prodScal(c));
a=Vecteur.somme(b,c);
System.out.print("b+c=");a.affiche();
}
}
126
0.00.00.00.00.0
0.50.50.50.50.5
a.b=0.0
1.252.55.253.01.0
b.c=6.5
b+c=1.753.05.753.51.5
Dansunprogrammeréel,onseraitamenéàprendreplusdeprécautions,notamment:
–s’assurerdanslepremierconstructeurquelavaleurdenestpositiveounulle(unevaleur nulle conduisant simplement à un tableau de taille nulle, ce qui n’est pasincorrect) ou traiter correctement l’exception NegativeArraySizeIndexExceptioncorrespondante;
– vérifier dans les méthodes recevant un tableau en argument que les référencescorrespondantes ne sont pas nulles ou traiter l’exception NullPointerExceptionrisquantd’apparaître.
OnpourraitégalementdéclencherdesexceptionscrééesspécifiquementpourlaclasseVecteur.
127
58 Synthèse:utilitairespourdestableauxdetableaux
Réaliseruneclasseutilitaireconcernantdestableauxdetableauxdevaleursdetypedoubleetcontenantlesméthodesstatiquessuivantes:•affiche(double t [][]) :affiche lesvaleursde t, à raisond’une ligned’écranpourunelignedutableau,
•booleanregulier(doublet[][]):testesiletableautestrégulier,c’est-à-diresitoutesseslignesontlamêmetaille,
• double [ ] sommeLignes (double t [ ] [ ]) : fournit un tableau de doublecorrespondantauxsommesdesdifférenteslignesdet,
•double[][]somme(double[][] t1,double[][] t2) : s’assureque lestableauxt1ett2sontréguliersetdemêmesdimensionsetfournitdanscecasleursommeenrésultat;danslecascontraire,ellefournituneréférencenulle.
Écrireunpetitprogrammedetest.
Rappelons que la notion de tableau à plusieurs indices n’existe pas en Java qui nedisposeenfaitquedelacompositiondestableaux:lesélémentsd’untableaupeuventêtre à leur tour des tableaux.Dans ce cas, il n’est pas nécessaire que les "tableauxéléments"soientdemêmetaille.S’ils lesont,onditquele tableauest"régulier" ; ilpermetalorsdesimulerletableauàplusieursindicesdelaplupartdesautreslangages.
classUtil2D
{publicstaticbooleanregulier(double[][]t)
{intn=t[0].length;//longueurpremiereligne
for(inti=1;i<t.length;i++)//parcourtleslignesapartir
//delaseconde
if(t[i].length!=n)returnfalse;
returntrue;
}
128
publicstaticdouble[]sommeLignes(double[][]t)
{intnLignes=t.length;
double[]res=newdouble[nLignes];
for(inti=0;i<nLignes;i++)
{res[i]=0.;
for(intj=0;j<t[i].length;j++)res[i]+=t[i][j];
}
returnres;
}
publicstaticdouble[][]somme(double[][]t1,double[][]t2)
{if(!regulier(t1)||!regulier(t2))returnnull;
if(t1.length!=t2.length)returnnull;
if(t1[0].length!=t2[0].length)returnnull;
intnLig=t1.length;intnCol=t1[0].length;
double[][]som=newdouble[nLig][nCol];
for(inti=0;i<nLig;i++)
for(intj=0;j<nCol;j++)
som[i][j]=t1[i][j]+t2[i][j];
returnsom;
}
publicstaticvoidaffiche(double[][]t)
{for(inti=0;i<t.length;i++)
{for(intj=0;j<t[i].length;j++)
System.out.print(t[i][j]+"");
System.out.println();
}
}
}
publicclassTUtil2D
{publicstaticvoidmain(Stringargs[])
{double[][]a={{1,2,3},{4,5,6}};
double[][]b={{6,5,4},{3,2,1}};
double[][]c=Util2D.somme(a,b);
System.out.println("a=");Util2D.affiche(a);
System.out.println("b=");Util2D.affiche(b);
System.out.println("c=");Util2D.affiche(c);
double[][]d={{1,2},{1,2,3},{1},{1,2,3,4,5}};
129
double[]sLig=Util2D.sommeLignes(d);
System.out.println("d=");Util2D.affiche(d);
System.out.print("sommelignesded=");
for(inti=0;i<sLig.length;i++)System.out.print(sLig[i]+"
");
}
}
a=
1.02.03.0
4.05.06.0
b=
6.05.04.0
3.02.01.0
c=
7.07.07.0
7.07.07.0
d=
1.02.0
1.02.03.0
1.0
1.02.03.04.05.0
sommelignesded=3.06.01.015.0
Commedansl’exercice57,nousn’avonspasprévudeprotectionscontrelesréférencesnulles fournies en argument. Celles-ci pourraient s’avérer nécessaires dans unprogrammeréel.
130
59 Synthèse:cribled’Eratosthène
Ilexisteuneméthodededéterminationdetouslesnombrespremierscomprisentre1etn,connuesouslenomde"cribled’Eratosthène".Elleconsisteàdresserunelistede tous les nombres entiers considérés et à y "rayer" tous les multiples d’autresentiers.Plusprécisément,onprocédeainsi:
•onrayele1(qui,pardéfinition,n’estpasunnombrepremier),
•onrecherche,àpartirduderniernombrepremierconsidéré(lapremièrefois,onconvientqu’ils’agitdu1),lepremiernombrenonrayé(onpeutmontrerqu’ilestpremier). Ildevient,à son tour, lederniernombrepremierconsidéréeton rayetoussesmultiples,
•on répète le traitementprécédent jusqu’àceque lenombrepremier considérésoit supérieur à la racine carrée de n. On peut alors démontrer que tous lesnombresnonpremiersontétérayésdelaliste.
Écrire un programme exploitant cette méthode pour rechercher tous les nombrespremierscomprisentre1etunevaleurfournieendonnée.
Nousreprésentonsle"crible"paruntableaudenbooléensnomméraye.Pourfaciliterleschoses,nousconvenonsqueraye[i]correspondaunombrei,cequinousimposededonnerautableaurayeladimensionnMax+1.Lavariablenombresertàreprésenterlederniernombrepremierconsidéré(dontonrayetouslesmultiples).Pour faciliter la lecturedes résultats, nous les affichons à raisondenParLigne (10)valeursparligne.
publicclassErato
{publicstaticvoidmain(String[]args)
{finalintnParLigne=10;
booleanraye[];//tableauservantde
"crible"
intnombre;//derniernombreentier
raye
intnMax;//leplusgrandentiera
examiner
131
inti;
/*preparationducrible*/
System.out.print("Donnezleplusgrandnombreentieraexaminer:
");
nMax=Clavier.lireInt();
raye=newboolean[nMax+1];
for(i=1;i<=nMax;i++)raye[i]=false;
/*onrayelenombre1*/
raye[1]=false;nombre=1;
while(nombre*nombre<=nMax)
{/*recherche,apartirdenombre,dupremiernombrenonraye*/
while((raye[++nombre])&&(nombre<=nMax)){}
/*onrayetoussesmultiples*/
for(i=2*nombre;i<=nMax;i+=nombre)raye[i]=true;
}
/*affichagedesresultats*/
System.out.println("entre1et"+nMax+"lesnombrespremiers
sont:");
intnAff=0;//nombredevaleursaffichees
for(i=1;i<=nMax;i++)
{if(!raye[i]){System.out.print(i+"");
nAff++;
if(nAff==nParLigne){nAff=0;
System.out.println();
}
}
}
}
}
Voiciunexempled’exécutiondeceprogramme:
Donnezleplusgrandnombreentieraexaminer:1000
entre1et1000lesnombrespremierssont:
123571113171923
29313741434753596167
717379838997101103107109
113127131137139149151157163167
173179181191193197199211223227
229233239241251257263269271277
132
281283293307311313317331337347
349353359367373379383389397401
409419421431433439443449457461
463467479487491499503509521523
541547557563569571577587593599
601607613617619631641643647653
659661673677683691701709719727
733739743751757761769773787797
809811821823827829839853857859
863877881883887907911919929937
941947953967971977983991997
Dans la boucle de recherche du premier nombre non rayé, nous avons conservé le"garde-fou"nombre<nMax.Onpourrait toutefoisdémontrerque,dèsquenMax estsupérieurouégalà2,onesttoujoursassurédetrouveraumoinsunnombrenonrayéavant la findu tableau (compte tenude ceque l’on commence l’exploration avecunnombreinférieurouégalàlaracinecarréedenMax).
133
Chapitre5
L’héritageetlepolymorphisme
Connaissancesrequises
•Définitiond’uneclassedérivée;lemotcléextends
•Droitsd’accèsd’uneclassedérivéeauxmembresdesaclassedebase
•Constructionetinitialisationdesobjetsdérivés;règlesd’appeldesconstructeurs;appelduconstructeurdelaclassedebasedepuisleconstructeurdelaclassedérivée:lemotclésuper
•Dérivationssuccessives
•Redéfinitiondeméthodesoudechamps;lasurdéfinitionàtraversl’héritage;utilisationsimultanéedespossibilitésdesurdéfinitionetderedéfinition;contraintesrelativesàlasurdéfinition
•Lepolymorphisme:sesfondementssurlaredéfinition;polymorphismeetsurdéfinition;conversiond’argumentseffectifsdetypeclasse;conversionsexplicitesderéférences;laréférencesuper(endehorsd’unconstructeur)
•LasuperclasseObject;référencesdetypeObject;laméthodeequals
•Lesmembresprotégés(protected)
•Classesetméthodesfinales
•Classesabstraites
•Interfaces;définition,implémentation;variablesdetypeinterface;constantesd’uneinterface;dérivationd’uneinterface
•Classesenveloppes:Boolean,Byte,Character,Short,Integer,Long,Float
134
etDouble
•Classesanonymes
135
60 Définitiond’uneclassedérivée,droitsd’accès(1)
Ondisposedelaclassesuivante:classPoint
{publicvoidinitialise(intx,inty){this.x=x;this.y=
y;}
publicvoiddeplace(intdx,intdy){x+=dx;y+=dy;}
publicintgetX(){returnx;}
publicintgetY(){returny;}
privateintx,y;
}
Réaliser une classe PointA, dérivée de Point disposant d’une méthode afficheaffichant(enfenêtreconsole)lescoordonnéesd’unpoint.EcrireunpetitprogrammeutilisantlesdeuxclassesPointetPointA.Quesepasserait-ilsilaclassePointnedisposaitpasdesméthodesgetXetgetY?
Il suffit de définir une classe dérivée en utilisant le mot clé extends. La méthodeaffiche,commetouteméthoded’uneclassedérivéeaaccèsàtouslesmembrespublicsdelaclassedebase,doncenparticulieràgetXetgetY.
classPointAextendsPoint
{voidaffiche()
{System.out.println("Coordonnees:"+getX()+""+getY());
}
}
OnpeutalorscréerdesobjetsdetypePointAetleurappliqueraussibienlesméthodespubliquesdePointAquecellesdePointcommedansceprogrammeaccompagnéd’unexempled’exécution:publicclassTsPointA
{publicstaticvoidmain(Stringargs[])
{Pointp=newPoint();
136
p.initialise(2,5);
System.out.println("Coordonnees:"+p.getX()+""+p.getY()
);
PointApa=newPointA();
pa.initialise (1, 8) ; // on utilise la methode initialise de
Point
pa.affiche();//etlamethodeaffichedePointA
}
}
Coordonnees:25
Coordonnees:25
Notez bien qu’un appel tel que p.affiche() conduirait à une erreur de compilationpuisquelaclassedep(Point)nepossèdepasdeméthodeaffiche.Si laclassePointn’avaitpasdisposédesméthodesd’accèsgetXetgetY, iln’auraitpas été possible d’accéder à ses champs privés x et y depuis la classe PointA. Iln’auraitdoncpasétépossibledeladoterdelaméthodeaffiche.L’héritagenepermetpasdecontournerleprinciped’encapsulation.
Commenosclassesnedisposentpasdeconstructeur,ilestpossibledecréerdesobjetssans les initialiser.Dans ce cas, leurs champs auront simplement une valeur "nulle",c’est-à-direicilavaleurentière0.
137
61 Définitiond’uneclassedérivée,droitsd’accès(2)
Ondisposedelaclassesuivante:classPoint
{ public void setPoint (int x, int y) { this.x = x ; this.y =
y;}
publicvoiddeplace(intdx,intdy){x+=dx;y+=dy;}
publicvoidaffCoord()
{System.out.println("Coordonnees:"+x+""+y);
}
privateintx,y;
}
RéaliseruneclassePointNom,dérivéedePointpermettantdemanipulerdespointsdéfinis par deux coordonnées (int) et un nom (caractère). On y prévoira lesméthodessuivantes:
• setPointNom pour définir les coordonnées et le nom d’un objet de typePointNom,
•setNompourdéfinirseulementlenomd’untelobjet,• affCoordNom pour afficher les coordonnées et le nom d’un objet de typePointNom.ÉcrireunpetitprogrammeutilisantlaclassePointNom.
Nousdéfinissonsuneclassedérivéeenutilisantlemotcléextends:classPointNomextendsPoint
DanscetteclassePointNom,nousintroduisonsunchamp(depréférenceprivé)destinéàcontenirlenomdupoint:
privatecharnom;
LaméthodesetNomesttriviale.Comptetenudel’encapsulationdesdonnéesdePoint,nos deux autres méthodes doivent absolument recourir aux méthodes publiques de
138
Point.Endéfinitive,voiciladéfinitiondenotreclassePoitnNom:
classPointNomextendsPoint
{publicvoidsetPointNom(intx,inty,charnom)
{setPoint(x,y);
this.nom=nom;
}
publicvoidsetNom(charnom)
{this.nom=nom;
}
publicvoidaffCoordNom()
{System.out.print("Pointdenom"+nom+"");
affCoord();
}
privatecharnom;
}
Voiciunprogrammed’utilisationdePointNom:
publicclassTsPointN
{publicstaticvoidmain(Stringargs[])
{Pointp=newPoint();
p.setPoint(2,5);
p.affCoord();
PointNompn1=newPointNom();
pn1.setPointNom(1,7,'A');//methodedePointNom
pn1.affCoordNom();//methodedePointNom
pn1.deplace(9,3);//methodedePoint
pn1.affCoordNom();//methodedePointNom
PointNompn2=newPointNom();
pn2.setPoint(4,3);//methodedePoint
pn2.setNom('B');//methodedePointNom
pn2.affCoordNom();//methodedePointNom
pn2.affCoord();//methodedePoint
}
}
Coordonnees:25
PointdenomACoordonnees:17
139
PointdenomACoordonnees:1010
PointdenomBCoordonnees:43
Coordonnees:43
1.Iciencore,commenosclassesnedisposentpasdeconstructeur,ilestpossibledecréerdesobjetssanslesinitialiser.Danscecas,leurschampsaurontsimplementunevaleur"nulle",c’est-à-direicilavaleurentière0pourlescoordonéesetlecaractèredecodenulpourlenom.2.CommelaclassePointnedisposepasdeméthodesd’accèsauxcoordonnées,onvoitque laméthodeaffCoordNom n’apasd’autrepossibilité quede recourir à laméthodeaffCoord dePoint, ce qui impose des contraintes sur la présentation desrésultats.Enparticulier,ilseraitimpossibled’affichersurunemêmelignelenomdupointavantlescoordonnées.
140
62 Héritageetappelsdeconstructeurs
Ondisposedelaclassesuivante(disposantcettefoisd’unconstructeur):classPoint
{publicPoint(intx,inty){this.x=x;this.y=y;}
publicvoidaffCoord()
{System.out.println("Coordonnees:"+x+""+y);
}
privateintx,y;
}
RéaliseruneclassePointNom,dérivéedePointpermettantdemanipulerdespointsdéfinis par leurs coordonnées (entières) et un nom (caractère).On y prévoira lesméthodessuivantes:
• constructeur pour définir les coordonnées et le nom d’un objet de typePointNom,
• affCoordNom pour afficher les coordonnées et le nom d’un objet de typePointNom.ÉcrireunpetitprogrammeutilisantlaclassePointNom.
Cet exercice est voisin de l’exercice 61mais, cette fois, les deux classes disposentd’un constructeur. Celui de la classe dérivée PointNom doit prendre en charge laconstructiondel’intégralitédel’objetcorrespondant,quitteàs’appuyerpourcelasurle constructeur de la classe de base (ce qui est indispensable ici puisque la classePointnedisposepasdeméthodesd’accès).Rappelonsquel’appelduconstructeurdelaclassedebase(faitàl’aidedumotclésuper)doitconstituerlapremièreinstructionduconstructeurdelaclassedérivée.Endéfinitive,voicicequepourraitêtreladéfinitiondenotreclassePointNom:
classPointNomextendsPoint
{publicPointNom(intx,inty,charnom)
{super(x,y);
this.nom=nom;
141
}
publicvoidaffCoordNom()
{System.out.print("Pointdenom"+nom+"");
affCoord();
}
privatecharnom;
}
Voiciunpetitprogrammed’utilisationdePointNom:
publicclassTsPointC
{publicstaticvoidmain(Stringargs[])
{PointNompn1=newPointNom(1,7,'A');
pn1.affCoordNom();//methodedePointNom
PointNompn2=newPointNom(4,3,'B');
pn2.affCoordNom();//methodedePointNom
pn2.affCoord();//methodedePoint
}
}
PointdenomACoordonnees:17
PointdenomBCoordonnees:43
Coordonnees:43
142
63 Redéfinition
Ondisposedelaclassesuivante:classPoint
{publicPoint(intx,inty){this.x=x;this.y=y;}
publicvoidaffiche()
{System.out.println("Coordonnees:"+x+""+y);
}
privateintx,y;
}
RéaliseruneclassePointNom,dérivéedePointpermettantdemanipulerdespointsdéfinis par leurs coordonnées et un nom (caractère).On y prévoira lesméthodessuivantes:
• constructeur pour définir les coordonnées et le nom d’un objet de typePointNom,
•affichepourafficherlescoordonnéesetlenomd’unobjetdetypePointNom.
Cet exercice est voisin de l’exercice 62. L’écriture du constructeur reste la même.Mais,cettefois,ondoitredéfinirlaméthodeaffichedanslaclassedérivée.L’affichagedunomn’yposeaucunproblème:System.out.print("Pointdenom"+nom+"");
Enrevanche,ilnousfautfaireappelàlaméthodeaffichedelaclassedebase.Pourcefaire,nousemployonslemotclésuper:super.affiche();
Endéfinitive,voici ladéfinitiondenotreclasse, accompagnéed’unpetitprogrammed’utilisation:
classPointNomextendsPoint
{publicPointNom(intx,inty,charNom)
{super(x,y);
this.Nom=Nom;
}
143
publicvoidaffiche()
{System.out.print("Pointdenom"+Nom+"");
super.affiche();
}
privatecharNom;
}
publicclassTsPointR
{publicstaticvoidmain(Stringargs[])
{Pointp=newPoint(3,7);
p.affiche();//methodedePoint
PointNompn=newPointNom(1,7,'A');
pn.affiche();//methodedePointNom
}
}
Coordonnees:37
PointdenomACoordonnees:17
Ici,laredéfinitiondelaméthodeaffichedansPointNomutiliselaméthodeaffichedelaclasseascendantePoint,cequiimposederecouriraumotclésuper.Bienentendu,iln’enirapastoujoursainsi:rienn’empêchederedéfinirentièrementuneméthodesanschercheràexploitercelledelaclasseascendante.
144
64 Constructionetinitialisationd’uneclassedérivée
Quelsrésultatsfournitceprogramme?classA
{
publicA(intnn)
{System.out.println("EntreeConstrA-n="+n+"p="+p);
n=nn;
System.out.println("SortieConstrA-n="+n+"p="+p);
}
public int n ; // ici, exceptionnellement, pas
d'encapsulation
publicintp=10;
}
classBextendsA
{publicB(intn,intpp)
{super(n);
System.out.println("EntreeConstrB-n="+n+"p="+p+"
q="+q);
p=pp;
q=2*n;
System.out.println("SortieConstrB-n="+n+"p="+p+"
q="+q);
}
publicintq=25;
}
publicclassTstInit
{publicstaticvoidmain(Stringargs[])
{Aa=newA(5);
Bb=newB(5,3);
}
}
145
Il faut tenir compte de l’ordre dans lequel ont lieu les initialisations des champs(expliciteetimplicite)etlesappelsdesconstructeurs,àsavoir:
•initialisationpardéfautdeschampsdel’objetdérivé(ycomprisceuxhérités),
•initialisationexplicitedeschampshérités,
•exécutionduconstructeurdelaclassedebase,
•initialisationexplicitedeschampsspécifiquesàl’objetdérivé,
•exécutionduconstructeurdelaclassedérivée.Celanousconduitauxrésultatssuivants:
EntreeConstrA-n=0p=10
SortieConstrA-n=5p=10
EntreeConstrA-n=0p=10
SortieConstrA-n=5p=10
EntreeConstrB-n=5p=10q=25
SortieConstrB-n=5p=3q=10
146
65 Dérivationssuccessivesetredéfinition
Quelsrésultatsfournitleprogrammesuivant?classA
{publicvoidaffiche()
{System.out.println("JesuisunA");
}
}
classBextendsA{}
classCextendsA
{publicvoidaffiche()
{System.out.println("JesuisunC");
}
}
classDextendsC
{publicvoidaffiche()
{System.out.println("JesuisunD");
}
}
classEextendsB{}
classFextendsC{}
publicclassDiagHeri
{publicstaticvoidmain(Stringarg[])
{Aa=newA();a.affiche();
Bb=newB();b.affiche();
Cc=newC();c.affiche();
Dd=newD();d.affiche();
Ee=newE();e.affiche();
Ff=newF();f.affiche();
}
}
147
Lorsd’unappeltelqueo.affiche()(oétantunobjetdel’unedesclassesconcernées),onrecherchetoutd’abordlaméthodeaffichedanslaclassedeo.Siaucuneméthoden’est trouvée, on poursuit la recherche dans la classe ascendante et ainsi de suitejusqu’àceque laméthode soit trouvée1 (si l’on arrive à la classeObject, racine detoutes les classes, sans que la méthode ne soit trouvée, on obtient une erreur decompilation).Ici,leprogrammefournitcesrésultats:
JesuisunA
JesuisunA
JesuisunC
JesuisunD
JesuisunA
JesuisunC
148
66 Dérivationssuccessivesetsurdéfinition
Quelsrésultatsfournitleprogrammesuivant?classA
{publicvoidf(doublex){System.out.print("A.f(double="+x
+")");}
}
classBextendsA{}
classCextendsA
{publicvoidf(longq){System.out.print("C.f(long="+q+")
");}
}
classDextendsC
{ public void f(int n) { System.out.print ("D.f(int=" + n + ")
");}
}
classEextendsB{}
classFextendsC
{publicvoidf(floatx){System.out.print("F.f(float="+x+
")");
}
public void f(int n) { System.out.print ("F.f(int=" + n + ")
");}
}
publicclassSurdf
{publicstaticvoidmain(Stringarg[])
{bytebb=1;shortp=2;intn=3;longq=4;
floatx=5.f;doubley=6.;
Aa=newA();a.f(bb);a.f(x);System.out.println();
Bb=newB();b.f(bb);a.f(x);System.out.println();
C c = new C() ; c.f(bb) ; c.f(q) ; c.f(x) ;
System.out.println();
D d = new D() ; d.f(bb) ; c.f(q) ; c.f(y) ;
System.out.println();
E e = new E() ; e.f(bb) ; e.f(q) ; e.f(y) ;
149
System.out.println();
Ff=newF();f.f(bb);f.f(n);f.f(x);f.f(y);f.f(p);
}
}
Ici,on fait intervenirà la fois la redéfinitiond’uneméthodeet sa surdéfinition.Pourrésoudreunappeldelaformeo.f(v)(oétantunobjetetvuneexpression),onrecherchetoutes les méthodes acceptables, à la fois dans la classe de o et dans toutes sesascendantes.Onutiliseensuite lesrègleshabituellesderecherchede lameilleure(etunique)méthode.Endéfinitive, leprogrammefournit lesrésultatssuivants(notezquecertainesconversionspeuventapparaître):
A.f(double=1.0)A.f(double=5.0)
A.f(double=1.0)A.f(double=5.0)
C.f(long=1)C.f(long=4)A.f(double=5.0)
D.f(int=1)C.f(long=4)A.f(double=6.0)
A.f(double=1.0)A.f(double=4.0)A.f(double=6.0)
F.f(int=1) F.f(int=3) F.f(float=5.0) A.f(double=6.0) C.f(long=4)
F.f(int=2)
150
67 Lesbasesdupolymorphisme
Quelsrésultatsfournitleprogrammesuivant?classA
{publicvoidaffiche(){System.out.print("JesuisunA");}
}
classBextendsA{}
classCextendsA
{publicvoidaffiche(){System.out.print("JesuisunC");}
}
classDextendsC
{publicvoidaffiche(){System.out.print("JesuisunD");}
}
classEextendsB{}
classFextendsC{}
publicclassPoly
{publicstaticvoidmain(Stringarg[])
{Aa=newA();a.affiche();System.out.println();
Bb=newB();b.affiche();
a=b;a.affiche();System.out.println();
Cc=newC();c.affiche();
a=c;a.affiche();System.out.println();
Dd=newD();d.affiche();
a=d;a.affiche();
c=d;c.affiche();System.out.println();
Ee=newE();e.affiche();
a=e;a.affiche();
b=e;b.affiche();System.out.println();
Ff=newF();f.affiche();
a=f;a.affiche();
c=f;c.affiche();
}
151
}
Certainespossibilitésd’affectationentreobjetsdestypesclassesA,B,C,D,EetFnefigurentpasdansleprogrammeci-dessus.Pourquoi?
EnJava, l’unedespropriétésdu"polymorphisme"estque l’appeld’uneméthodeestdéterminé au moment de l’exécution, suivant la nature de l’objet effectivementréférencé(etnonseulementsuivantletypedelaréférence).C’estpourquoiicitouslesappelsdeafficheconcernantunmêmeobjetfournissentlemêmemessage,quelquesoitletypederéférenceutilisé:
JesuisunA
JesuisunAJesuisunA
JesuisunCJesuisunC
JesuisunDJesuisunDJesuisunD
JesuisunAJesuisunAJesuisunA
JesuisunCJesuisunCJesuisunC
Néanmoins,uneréférencedetypeTnepeutsevoiraffecterqu’uneréférenced’untypeToudérivédeT.C’estcequisepassaiteffectivementdansnotreprogramme.Mais(ensupposantlesmêmesdéclarations),cesaffectationsseraientincorrectes:
b=a;e=a;e=b;c=a;d=c;d=a;f=c;f=a;
b=c;b=d;b=f;e=c;e=d;e=f;c=b;c=e;d=b;d=e;f=b;
f=e;
152
68 Polymorphismeetsurdéfinition
Quelsrésultatsfournitleprogrammesuivant?classA
{publicvoidf(doublex){System.out.print("A.f(double="+x
+")");}
}
classBextendsA{}
classCextendsA
{publicvoidf(longq){System.out.print("C.f(long="+q+")
");}
}
classDextendsC
{ public void f(int n) { System.out.print ("D.f(int=" + n + ")
");}
}
classFextendsC
{publicvoidf(floatx){System.out.print("F.f(float="+x+
")");}
public void f(int n) { System.out.print ("F.f(int=" + n + ")
");}
}
publicclassPolySur
{publicstaticvoidmain(Stringarg[])
{bytebb=1;shortp=2;intn=3;longq=4;
floatx=5.f;doubley=6.;
System.out.println("**A**");
Aa=newA();a.f(bb);a.f(x);System.out.println();
System.out.println("**B**");
Bb=newB();b.f(bb);b.f(x);System.out.println();
a=b;a.f(bb);a.f(x);System.out.println();
System.out.println("**C**");
C c = new C() ; c.f(bb) ; c.f(q) ; c.f(x) ;
System.out.println();
153
a=c;a.f(bb);a.f(q);a.f(x);System.out.println();
System.out.println("**D**");
D d = new D() ; d.f(bb) ; c.f(q) ; c.f(y) ;
System.out.println();
a=c;a.f(bb);a.f(q);a.f(y);System.out.println();
System.out.println("**F**");
Ff=newF();f.f(bb);f.f(n);f.f(x);f.f(y);
System.out.println();
a = f ; a.f(bb) ; a.f(n) ; a.f(x) ; a.f(y) ;
System.out.println();
c=f;c.f(bb);c.f(n);c.f(x);c.f(y);
}
}
Ici,oncombine:
•lespossibilitésqu’offrelepolymorphismedechoisiruneméthodesuivantlanaturedel’objeteffectivementréférencé,
•lespossibilitésdesurdéfinitionquipermettentdedétermineruneméthodesuivantletypedesesarguments.
Mais il faut bien voir que le choix d’une méthode surdéfinie est réalisé par lecompilateur, alors que la ligature dynamique induite par le polymorphisme nes’effectuequ’àl’exécution.Plus précisément, lors d’un appel du type o.f(…), la signature de la méthode f estdéfinieàlacompilationauvudesonappel,enutilisantletypedelavariableo(etnonle type de l’objet référencé, non encore connu) et en appliquant éventuellement lesrèglesdechoixd’uneméthodesurdéfinie.Cechoixnepeutalorssefairequedanslaclasse deo ou ses ascendantes (et en aucun cas dans ses descendantes éventuelles,commelepermettralaligaturedynamique).Au moment de l’exécution, on cherchera parmi la classe de l’objet effectivementréférencéparo(quipeutdoncéventuellementêtreuneclassedescendantedecelledeo), uneméthode ayant la signature précédemment déterminée.Mais, on ne reviendraplussurlechoixdelameilleureméthode.Parexemple,dansletroisièmegrouped’instructions(**C**),lesappelsdelaformec.f(…) sont traités en considérant les méthodes f deC et de son ascendante A. Enrevanche, malgré l’affectation a=c, ceux de la forme a.f(…) sont traités en ne
154
considérantquelesméthodesfdeA.Ainsi,l’appelc.f(bb)utiliseC.f(long)tandisquel’appela.f(bb)utiliseA.f(double).Finalement,leprogrammefournitlesrésultatssuivants:
**A**
A.f(double=1.0)A.f(double=5.0)
**B**
A.f(double=1.0)A.f(double=5.0)
A.f(double=1.0)A.f(double=5.0)
**C**
C.f(long=1)C.f(long=4)A.f(double=5.0)
A.f(double=1.0)A.f(double=4.0)A.f(double=5.0)
**D**
D.f(int=1)C.f(long=4)A.f(double=6.0)
A.f(double=1.0)A.f(double=4.0)A.f(double=6.0)
**F**
F.f(int=1)F.f(int=3)F.f(float=5.0)A.f(double=6.0)
A.f(double=1.0)A.f(double=3.0)A.f(double=5.0)A.f(double=6.0)
C.f(long=1)C.f(long=3)A.f(double=5.0)A.f(double=6.0)
155
69 Leslimitesdupolymorphisme
SoitlesclassesPointetPointNomainsidéfinies:classPoint
{publicPoint(intx,inty){this.x=x;this.y=y;}
publicstaticbooleanidentiques(Pointa,Pointb)
{return((a.x==b.x)&&(a.y==b.y));}
publicbooleanidentique(Pointa)
{return((a.x==x)&&(a.y==y));}
privateintx,y;
}
classPointNomextendsPoint
{PointNom(intx,inty,charnom)
{super(x,y);this.nom=nom;}
privatecharnom;
}
1.Quelsrésultatsfournitceprogramme?Expliciterlesconversionsmisesenjeuetlesrèglesutiliséespourtraiterlesdifférentsappelsdeméthodes:publicclassLimPoly
{publicstaticvoidmain(Stringargs[])
{Pointp=newPoint(2,4);
PointNompn1=newPointNom(2,4,'A');
PointNompn2=newPointNom(2,4,'B');
System.out.println(pn1.identique(pn2));
System.out.println(p.identique(pn1));
System.out.println(pn1.identique(p));
System.out.println(Point.identiques(pn1,pn2));
}
}
2.DoterlaclassePointNomd’uneméthodestatiqueidentiquesetd’uneméthodeidentique fournisant toutes les deux la valeur true lorsque les deux pointsconcernésontàlafoismêmescoordonnéesetmêmenom.Quelsrésultatsfourniraalorsleprogrammeprécédent?Quellesserontlesconversionsmisesenjeuetlesrèglesutilisées?
156
Question1pn1.identique(pn2)Lors de la compilation, on recherche uneméthode identique dans la classe de pn1(PointNom)ousesascendantes.OnentrouveuneseuledansPointavecunargumentdetypePoint, ce qui fige sa signature sous la forme identique(Point), en imposant uneconversion implicite de pn2 en Point. Lors de l’exécution, on cherche une telleméthoded’aborddansPointNom(ligaturedynamique)puis,commeonn’entrouvepas,dansPoint.Endéfinitive,onexécutebienlaméthodeidentiquedePoint.
p.identique(pn1)Lorsdelacompilation,ontrouvelaméthodeidentiquedanslaclassedep(Point),cequi fige sa signature sous la forme identique(Point), en imposant une conversionimplicitedepn1enPoint.Lorsde l’exécution,onchercheune telleméthodedans laclassedep(Point).Endéfinitive,onexécutebienlaméthodeidentiquedePoint.
pn1.identique(p)Lors de la compilation, on recherche uneméthode identique dans la classe de pn1(PointNom)ousesascendantes.OnentrouveuneseuledansPointavecunargumentdetypePoint,cequi figesasignaturesous la forme identique(Point) (cette fois aucuneconversiond’argumentn’estprévue).Lorsdel’exécution,onchercheunetelleméthoded’aborddansPointNom(ligaturedynamique)puisdansPoint.Endéfinitive,onexécutebienlaméthodeidentiquedePoint.
Point.identiques(pn1,pn2)Ici, l’appelest résoludès lacompilation (lesméthodesstatiquesnepeuventpasêtreconcernéesparlepolymorphisme).Ilfaitintervenirlaconversiondepn1etdepn2enPoint.Commeonpeuts’yattendre,leprogrammefournitcesrésultats:
true
true
true
true
Question2Comme les champs x et y dePoint ne sont pas publics et comme l’on ne disposed’aucune méthode d’accès, il est nécessaire, au sein des méthodes voulues dansPointNom,derecourirauxméthodescorrespondantesdePoint:
157
publicstaticbooleanidentiques(PointNoma,PointNomb)
{
return(Point.identiques(a,b)&&(a.nom==b.nom));
}
publicbooleanidentique(PointNoma)
{
return(super.identique(a)&&(nom==a.nom));
}
Onnoteralanotationsuper.identiquequiforcel’utilisationdelaméthodeidentiquedelaclasseascendantePoint.
pn1.identique(pn2)Lors de la compilation, on recherche uneméthode identique dans la classe de pn1(PointNom)ousesascendantes.Cettefois,lesméthodesdePointNometdePointsontacceptables.Mais, la première estmeilleure, ce qui fige la signature de laméthodeappeléesouslaforme identique(PointNom).Lorsdel’exécution,oncherched’abordune telleméthode dansPointNom et on la trouve. En définitive, on exécute bien laméthodeidentiquedePointNom,contrairementàcequesepassaitdanslaquestion1.
p.identique(pn1)Lorsdelacompilation,cettefois,onrechercheuneméthodeidentiquedanslaclassedep(Point),cequifigesasignaturesouslaformeidentique(Point),enimposantuneconversion implicite de pn1 en Point. Lors de l’exécution, on cherche une telleméthode dans la classe de p (Point). En définitive, on exécute (comme dans lapremière question) la méthode identique de Point. Notez que l’application de laméthodedePointNomn’aurait,detoutesfaçons,aucunesignification,l’objetpn’ayantpasdechampnom!
pn1.identique(p)Lors de la compilation, on recherche, comme avec le premier appel, une méthodeidentiquedanslaclassedepn1(PointNom)ousesascendantes.Mais,cettefois,seulecelledePointestacceptablecaronnepeutpasconvertirimplicitementletypePointenPointNom (seul l’inverse est possible). On fige donc la signature de laméthodeappeléesouslaformeidentique(Point)(cettefoisaucuneconversiond’argumentn’estprévue). Lors de l’exécution, on cherche une telleméthode d’abord dansPointNom(ligature dynamique) puis dans Point. En définitive, on exécute bien la méthodeidentiquedePoint.
158
Point.identiques(pn1,pn2)Ici, comme précédemment, l’appel est résolu dès la compilation et il fait toujoursintervenir la conversiondepn1 et depn2 enPoint. Comme on peut s’y attendre, leprogrammefournitcesrésultats:
false
true
true
true
Notez que l’on pourrait forcer l’emploi de identiques de PointNom en écrivantPointNom.identiques(pn1,pn2) ;danscecas, iln’yauraitplusdeconversionet l’onobtiendraitlerésultatfalse.
159
70 Classeabstraite
On souhaite disposer d’une hiérarchie de classes permettant de manipuler desfiguresgéométriques.Onveutqu’ilsoittoujourspossibled’étendrelahiérarchieendérivantdenouvellesclassesmaisonsouhaitepouvoir imposerquecesdernièresdisposenttoujoursdesméthodessuivantes:•voidaffiche()•voidhomothetie(doublecoeff)•voidrotation(doubleangle)Écrire laclasseabstraiteFigure qui pourra servir de classe debase à toutes cesclasses.
Ilsuffitd’appliquerlesrèglesdedéfinitiond’uneclasseabstraite.Onyplacelesen-têtes desméthodes qu’on souhaite voir redéfinies dans les classes dérivées, en leurassociantlemotcléabstract:
abstractclassFigure
{abstractpublicvoidaffiche();
abstractpublicvoidhomothetie(doublecoef);
abstractpublicvoidrotation(doubleangle);
}
Lemot clé abstract figurant devant class peut être omis (toute classe disposant aumoinsd’uneméthodeabstraiteestabstraite).Ilestcependantconseillédeleconserver.Quant aux noms d’arguments accompagnant les en-têtes de méthodes, ils sontsyntaxiquementnécessaires(bienquen’ayantaucunesignification).LesclassesdelahiérarchiedefiguresserontalorssimplementdéfiniescommeclassesdérivéesdeFigure et ellesdevrontdéfinir les troisméthodesaffiche,homothetie etrotation,parexemple:
classPointextendsFigure
{publicvoidaffiche(){.....}
publicvoidhomothetie(doublecoef){.....}
160
publicvoidrotation(doubleangle){.....}
.....
}
161
71 Classeabstraiteetpolymorphisme
Compléter la classe abstraite Figure de l’exercice précédent, de façon qu’elleimplémente:
•uneméthodehomoRot (doublecoef,doubleangle) qui applique à la fois unehomothétieetunerotationàlafigure,
• de méthodes statiques afficheFigures, homothetieFigures et rotationFiguresappliquantunemêmeopération(affichage,homothétieourotation)àuntableaudefigures(objetsd’uneclassedérivéedeFigure).
Une classe abstraite peut comporter des définitions deméthodes (non abstraites) quipourrontalorsêtreutiliséesparlesclassesdérivéessansqu’ilnesoitnécessairedelesredéfinir (mais on peut toujours le faire !). D’autre part, une classe abstraite peutcomporterdesméthodesstatiques,pourpeuquecelles-cinesoientpasabstraites(cequin’auraitaucunesignification).Endéfinitive,voiciladéfinitiondenotrenouvelleclasseFigure:
abstractclassFigure
{abstractpublicvoidaffiche();
abstractpublicvoidhomothetie(doublecoef);
abstractpublicvoidrotation(doubleangle);
publicvoidHomoRot(doublecoef,doubleangle)
{homothetie(coef);rotation(angle);
}
publicstaticvoidafficheFigures(Figure[]f)
{for(inti=0;i<f.length;i++)f[i].affiche();
}
publicstaticvoidhomothetieFigures(doublecoef,Figure[]f)
{for(inti=0;i<f.length;i++)f[i].homothetie(coef);
}
publicstaticvoidrotationFigures(doubleangle,Figure[]f)
162
{for(inti=0;i<f.length;i++)f[i].rotation(angle);
}
}
Onnoteraque,auseindelaméthodehomoRot,ilestpossibled’appelerlesméthodeshomotethie et rotation, et ceci bien qu’elles soient abstraites. En effet, d’après lesrèglesdupolymorphisme, laméthodeeffectivementappelée seracellecorrespondantau type effectif de l’objet ayant appelé laméthode homoRot ; grâce aux contraintesportantsurlesdérivéesdeclassesabstraittes,onestcertainqu’elleexistera.Desréflexionsanaloguess’appliquentà l’appeldesméthodeshomothetieetrotationdanslesméthodesstatiqueshomothetieFiguresetrotationFigures.
163
72 Interface
Onsouhaitedisposerdeclassespermettantdemanipulerdesfiguresgéométriques.On souhaite pouvoir caractériser celles qui possèdent certaines fonctionnalités enleurdemandantd’implémenterdesinterfaces,àsavoir:
•Affichablepourcellesquidisposerontd’uneméthodevoidaffiche(),•Tranformablepourcellesquidisposerontdesdeuxméthodessuivantes:
voidhomothetie(doublecoeff)voidrotation(doubleangle)
ÉcrirelesdeuxinterfacesAffichableetTransformable.
Ilsuffitd’appliquerlesrèglesdedéfinitiond’uneinterface,cequinousconduità:
interfaceAffichable
{abstractpublicvoidaffiche();
}
interfaceTransformable
{abstractpublicvoidhomothetie(doublecoef);
abstractpublicvoidrotation(doubleangle);
}
Ici,nosinterfacesdisposentd’undroitd’accèsdepaquetage.Commelesclasses,ellespourraientêtredéclaréespublicLesmotsclésabstractetpublicfigurantdanslesen-têtesdeméthodespeuventêtreomispuisque,paressence,lesméthodesd’uneinterfacesontpubliquesetabstraites.Une classe représentant une figure pourra implémenter aucune, une ou les deuxinterfacesprécédentes.Parexemple
classPointimplementsAffichable
{publicvoidaffiche(){.....}
}
classRectangleimplementsAffichable,Transformable
{publicvoidaffiche(){.....}
164
publicvoidhomothetie(doublecoef){.....}
publicvoidrotation(doubleangle){.....}
}
165
73 Synthèse:comparaisonentrehéritageetobjetmembre
Ondisposedelaclassesuivante:classPoint
{publicPoint(doublex,doubley){this.x=x;this.y=y;}
publicvoiddeplace(doubledx,doubledy){x+=dx;y+=dy;}
publicvoidaffiche()
{System.out.println("Pointdecoordonnees"+x+""+y);
}
publicdoublegetX(){returnx;}
publicdoublegetY(){returny;}
privatedoublex,y;
}
OnsouhaiteréaliseruneclasseCercledisposantdesméthodessuivantes:
• constructeur recevant en argument les coordonnées du centre du cercle et sonrayon,
•deplaceCentrepourmodifierlescoordonnéesducentreducercle,
•changeRayonpourmodifierlerayonducercle,
•getCentrequifournitenrésultatunobjetdetypePointcorrespondantaucentreducercle,
•affichequiaffichelescoordonnéesducentreducercleetsonrayon.1.DéfinirlaclasseCerclecommeclassedérivéedePoint.2.DéfinirlaclasseCerclecommepossédantunmembredetypePoint.Dans les deux cas, on écrira un petit programme mettant en jeu les différentesfonctionnalitésdelaclasseCercle.
ClassedérivéedePoint
166
classCercleextendsPoint
{publicCercle(doublex,doubley,doubler)
{super(x,y);
this.r=r;
}
publicvoiddeplaceCentre(doubledx,doubledy)
{super.deplace(dx,dy);
}
publicvoidchangeRayon(doubler)
{this.r=r;
}
publicPointgetCentre()
{Pointcentre=newPoint(getX(),getY());
returncentre;
}
publicvoidaffiche()
{ System.out.println ("Cercle de centre " + super.getX() + " " +
super.getY()
+"etderayon"+r);
}
privatedoubler;
}
Voici un petit programme d’utilisation de Cercle, accompagné du résultat de sonexécution:
publicclassTstCerD
{publicstaticvoidmain(Stringargs[])
{Cerclec=newCercle(3,8,2.5);
c.affiche();
c.deplaceCentre(1,0.5);
c.changeRayon(5.25);
c.affiche();
Pointa=c.getCentre();
a.affiche();
}
}
Cercledecentre3.08.0etderayon2.5
Cercledecentre4.08.5etderayon5.25
Pointdecoordonnees4.08.5
167
Avecunobjetmembre
classCercle
{publicCercle(doublex,doubley,doubler)
{centre=newPoint(x,y);
this.r=r;
}
publicvoiddeplaceCentre(doubledx,doubledy)
{centre.deplace(dx,dy);
}
publicvoidchangeRayon(doubler)
{this.r=r;
}
publicPointgetCentre()
{returncentre;
}
publicvoidaffiche()
{System.out.println("Cercledecentre"+centre.getX()+""+
centre.getY()
+"etderayon"+r);
}
privatePointcentre;
privatedoubler;
}
Le précédent programme d’utilisation deCercle peut encore être employé ici sansmodifications.Ilfournitlesmêmesrésultats.
1.Notezbienqu’ici aucunepossibilité de surdéfinitionn’existe puisqueaffiche nepossède aucun argument.Enrevanche, nous verrons dans les exercices suivants des situations dans lesquelles il peut être nécessaire deconsidérerplusieursméthodesappartenantàlafoisàlaclasseetàsesascendantes.
168
Chapitre6
LaclasseStringetleschaînesdecaractères
Connaissancesrequises
•LaclasseString:constructeurs,propriétésdesobjetsdetypeString,affectation
•Affichaged’unechaîneparprintouprintln
•Longueurd’unechaîne:méthodelength
•Accèsauxcaractèresd’unechaîne:méthodecharAt
•Concaténationdechaînesavecl’opérateur+;conversionsdesopérandes;l’opérateur+=
•Recherchedansunechaîne:méthodesindexOfetlastIndexOf
•Comparaisonsdechaînes:méthodesequalsetcompareTo
•Créationd’unechaîneparmodificationd’uneautre:méthodesreplace,substring,toLowerCase,toUpperCaseettrim
•Conversiond’untypeprimitifentypechaîne:méthodevalueOf
•Conversiond’unechaîneenuntypeprimitifàl’aidedesméthodesdesclassesenveloppesdestypesprimitifs
•LaméthodetoStringdelaclasseObject
•Conversionsentrechaînesettableauxdecaractères
•Argumentsdelalignedecommande
169
Note : on suppose qu’on dispose d’une classe nommée Clavier, disposant (entreautres) de méthodes (statiques) de lecture au clavier d’informations de type int(lireInt),float(lireFloat),double(lireDouble),char(lireChar)etString(lireString).Cetteclasseestprésentesur le siteWebd’accompagnementet sa listeest fournieenAnnexeD.
170
74 Constructionetaffectationdechaînes
Quelsrésultatsfournitleprogrammesuivant?publicclassChaine
{publicstaticvoidmain(Stringargs[])
{Stringch1=newString();
System.out.println("A-ch1=:"+ch1+":");
Stringch2="hello";
System.out.println("B-ch2=:"+ch2+":");
Stringch3=newString("bonjour");
System.out.println("C-ch3=:"+ch3+":");
Stringch4=newString(ch3);
System.out.println("D-ch4=:"+ch4+":");
ch3="bonsoir";
System.out.println ("E - ch4 =:" + ch4 + ": ch3 =:" + ch3 +
":");
ch4=ch3;
ch3="aurevoir";
System.out.println ("F - ch4 =:" + ch4 + ": ch3 =:" + ch3 +
":");
}
}
L’instructionStringch1=newString();
créeunechaînevideetplacesaréférencedansch1.L’instructionStringch2="hello";
créeunechaîneforméedescinqcaractèresh,e,l,letoetplacesaréférencedansch2.DemêmeStringch3=newString("bonjour");
171
créeunechaînecontenantlesseptcaractèresb,o,n,j,o,uetretplacesa référencedansch3.L’instructionStringch4=newString(ch3);
crée une chaîne par recopie de la valeur de la chaîne de référence ch3 et place saréférencedansch4.On notera bien que dorénavant, il existe deux chaînes de même contenu, commel’illustreceschéma:
L’affectationch3="bonsoir";
crée la chaînebonsoir et place sa référence dans ch3. L’ancienne chaîne (contenantbonjour)désignéeparch3n’étantplusréférencée,elledevientcandidateauramasse-miettes.Lasituationseprésenteainsi:
Aprèsl’affectationch4=ch3;
Lasituationseprésenteainsi:
172
Enfin,aprèsch3="aurevoir";
Onobtient:
Endéfinitive,leprogrammeaffichelesrésultatssuivants:
A-ch1=::
B-ch2=:hello:
C-ch3=:bonjour:
D-ch4=:bonjour:
E-ch4=:bonjour:ch3=:bonsoir:
F-ch4=:bonsoir:ch3=:aurevoir:
173
75 Accèsauxcaractèresd’unechaîne
Écrireunprogrammequilitunechaîneauclavieraetquienaffiche:
•uncaractèresurdeux(lepremierétantaffiché),
•lepremieretlederniercaractère.
a.OnpourrautiliserlaclasseClavier(voirnoteendébutdechapitre).
Ilsuffitd’utiliserlesméthodeslengthetcharAtdelaclasseString.
publicclassCarCh
{publicstaticvoidmain(Stringargs[])
{System.out.print("donnezunechaine:");
Stringch=Clavier.lireString();
System.out.print("uncaracteresurdeux:");
for(inti=0;i<ch.length();i+=2)
System.out.print(ch.charAt(i));
System.out.println();
System.out.println("Premiercaractere="+ch.charAt(0));
System.out.println ("Dernier caractere = " +
ch.charAt(ch.length()-1));
}
}
donnezunechaine:javaestplusportablequeC++
uncaracteresurdeux:jvslspralu+
Premiercaractere=j
Derniercaractere=+
Notezbienquelederniercaractèredelachaînechpossèdelerangch.length-1.Unetentative d’accès au caractère de rang ch.length conduirait à une exceptionStringIndexOutOfBoundsException.
174
76 Conversiond’unentierenchaîne
Écrire un programme qui lit un entier au claviera et qui l’affiche verticalementcommedanscetexemple:
donnezunnombreentier:785412
7
8
5
4
1
2
a.OnpourrautiliserlaclasseClavier(voirnoteendébutdechapitre).
Onpeutconvertirunentierenunechaîneàl’aidedelaméthodevalueOfdelaclasseString. L’accès aux caractères de la chaîne se fait avec laméthode charAt, d’où leprogramme:
publicclassConver
{publicstaticvoidmain(Stringargs[])
{System.out.print("donnezunnombreentier:");
intn=Clavier.lireInt();
Stringch=String.valueOf(n);
for(inti=0;i<ch.length();i++)//ou(depuisJDK5.0):
System.out.println(ch.charAt(i));//for(charc:ch)
}//System.out.println
(c);
}
Ici, nous avons utilisé laméthode valueOf pour convertir un entier en chaîne.Nousaurionspuégalement exploiter lapropriétéde l’opérateur+qui, lorsque l’unde sesdeuxopérandesestdetypeString,convertitl’autredanscemêmetype.C’estainsique
175
nous aurions pu écrire (un peu artificiellement) ch = "" + n. Notez cependant quel’affectation directe ch = n ne serait pas correcte puisque le type int n’est pascompatibleparaffectationavecletypeString.
176
77 Comptagedesvoyellesd’unmot
Écrireunprogrammequilitunmotauclavieraetquiindiquecombiendefoissontprésentes chacune des voyelles a, e, i,o,u ou y, que celles-ci soient écrites enmajusculesouenminuscules,commedanscetexemple:
donnezunmot:Anticonstitutionnellement
ilcomporte
1foislalettrea
3foislalettree
3foislalettrei
2foislalettreo
1foislalettreu
0foislalettrey
a.OnpourrautiliserlaclasseClavier(voirnoteendébutdechapitre).
Oncommencepar convertir tous les caractèresdumot enminuscules (par exemple).Puisoncomparechaquecaractère(obtenuparcharAt)avecchacunedessixvoyellesque l’on a placées dans un tableau de caractères. Un tableau de six entiers sert aucomptage.publicclassVoyelles
{publicstaticvoidmain(Stringargs[])
{charvoy[]={'a','e','i','o','u','y'};
intnVoy[]=newint[voy.length];
for(inti=0;i<nVoy.length;i++)nVoy[i]=0;
System.out.print("donnezunmot:");
Stringmot=Clavier.lireString();
mot=mot.toLowerCase();
for(inti=0;i<mot.length();i++)
for(intj=0;j<voy.length;j++)
if(mot.charAt(i)==voy[j])nVoy[j]++;
System.out.println("ilcomporte:");
for(inti=0;i<voy.length;i++)
System.out.println(nVoy[i]+"foislalettre"+voy[i]);
177
}
}
L’instruction:mot=mot.toLowerCase();
créeunenouvellechaîneobtenueparconversionenminusculesdelachaîneréférencéeparmot, puis place son adresse dansmot. Il n’y a pas modification de la chaîneinitiale. Ici, toutefois, celle-ci devenant non référencée, deviendra candidate auramasse-miettes…Notez que lorsqu’une voyelle est détectée, le programme poursuit inutilement lacomparaison du caractère concerné avec les éventuelles voyelles suivantes. Onpourraitl’éviterenutilisantuneinstructionbreakdanslabouclelaplusinterne.
178
78 Argumentsdelalignedecommande
Écrireunprogrammequirécupèredeuxentierssurla"lignedecommande"etquienaffichelasommeenfenêtreconsole,commedanscetexemple:
12+25=37
On vérifiera que les arguments fournis sont formés uniquement de chiffres (sansaucunsigne);danslecascontraire,leprogrammes’interrompra.
Lesargumentsdelalignedecommandesonttransmisàlaméthodemain,parlebiaisdesonuniqueargumentquisetrouveêtreuntableauderéférencessurdeschaînes.Nousvérifionstoutd’abordquecetableauestdetaille2.Sicen’estpaslecas,nousinterromponsleprogrammeenappelantlaméthodeSystem.exit.Puisnousnousassuronsquetouslescaractèresdesdeuxchaînessontbiendeschiffres(caractères de 0 à 9). Pour ce faire, nous utilisons ici la méthode substring pourextrairechaquecaractèredel’argumentsousformed’unechaînedelongueurun;celle-ciestalorscomparée(parequals1)avecchacunedeschaînesobtenuesenconvertissantchacun des nombres 0 à 9 en une chaîne (notez que l’on ne peut pas comparerdirectementunechaînedelongueur1avecuncaracètre).Làencore,si lesconditionsvouluesnesontpasremplies,nousinterromponsleprogramme.Enfin,nousconvertissonslesdeuxarguments(ainsicontrôlés)àl’aidedelaméthodeparseIntdelaclasseenveloppeInteger.
publicclassArgLC2
{publicstaticvoidmain(Stringargs[])
{
intnbArgs=args.length;
if (nbArgs != 2) { System.out.println ("nombre arguments
incorrect");
System.exit(-1);
}
//onverifiequelescaracteresdeargs[0]etargs[1]
//sontbiendeschiffres
for(inti=0;i<2;i++)
179
comp:for(intj=0;j<args[i].length();j++)
{for(intk=0;k<=9;k++)
if (args[i].substring(j,j+1).equals(String.valueOf(k)))
breakcomp;
System.out.println("argumentspastousnumeriques");
System.exit(-1);
}
intn1=Integer.parseInt(args[0]);
intn2=Integer.parseInt(args[1]);
System.out.println(n1+"+"+n2+"="+(n1+n2));
}
}
12+25=37
Laligne:if(args[i].substring(j,j+1).equals(String.valueOf(k)))breakcomp;
pourraitêtreremplacéepar:if(args[i].charAt(j)==chif[k])breakcomp;
avec,parexemple:char[]chif={'0','1','2','3','4','5','6','7','8','9'};
180
79 RedéfinitiondetoString
1. Réaliser une classe PointN permettant de manipuler des points d’un plan àcoordonnées entières et repérés par un nom de type chaîne. On se limitera à unconstructeuretàuneméthodeafficheaffichantlenomdupointetsescoordonnées,demanièrequelesinstructionssuivantes:
PointNa=newPointN(2,5,"orig");
System.out.print("a=");a.affiche();
fournissentlesrésultatssuivants:a=Pointnommeorigetdecoordonnees25
2. Modifier la classe précédente, de manière que les résultats suivants puissentmaintenants’obtenirainsi(onpourrasupprimerlaméthodeaffiche):
PointNa=newPointN(2,5,"orig");
System.out.println("a="+a);
Question1LadéfinitiondelaclassePointNneprésentepasdedifficultés:
classPointN
{publicPointN(intx,inty,Stringnom)
{this.x=x;this.y=y;this.nom=nom;
}
publicvoidaffiche()
{System.out.println("Pointnomme"+nom
+"etdecoordonnees"+x+""+y);
}
privateintx,y;
privateStringnom;
}
Notezqu’iln’estpasnécessairederecopierauseindel’objet lavaleurdelachaîne
181
représentant lenomdupoint.Onpeut secontenterd’en recopier la référencecar lesobjetsdetypeStringnesontpasmodifiables.
Question2La classeObject, dont dérive toute classe, dispose d’uneméthode toString qui, pardéfaut, affiche le nom de la classe et l’adresse de l’objet concerné. Si nous nemodifionspasnotreclassePointN,uneinstructiontelleque:System.out.println("a="+a);
appellera cette méthode toString pour permettre à l’opérateur + de convertir a enString;elleafficheraquelquechosecomme:a=PointN@fd7a8b04
Pourobtenirlesrésultatsvoulus,ilnoussuffitenfaitderedéfinirdefaçonappropriéelaméthodetoStringdansnotreclassePointN:classPointN
{publicPointN(intx,inty,Stringnom)
{this.x=x;this.y=y;this.nom=newString(nom);
}
publicStringtoString()
{Stringch="Jesuisunpointnomme"+nom
+"etdecoordonnees"+x+""+y;
returnch;
}
privateintx,y;
privateStringnom;
}
182
80 Synthèse:conjugaisond’unverbe
Écrireunprogrammequi litauclavieraunverbedupremiergroupe (il s’assureraqu’il est bien terminé par er) et qui en affiche la conjuguaison au présent del’indicatif.Onsupposeraqu’ils’agitd’unverberégulier.Autrementdit,onadmettraquel’utilisateurnefournitpasunverbetelquemanger(danscecas,leprogrammeafficheranousmangons!).Lesrésultatsseprésenterontainsi:
donnezunverberegulierdupremiergroupe:dire
***ilneseterminepasparer-donnez-enunautre:chanter
jechante
tuchantes
il/ellechante
nouschantons
vouschantez
ils/elleschantent
a.OnpourrautiliserlaclasseClavier(voirnoteendébutdechapitre).
On lira bien sûr le verbe sous la forme d’une chaîne de caractères. À l’aide de laméthodesubstring,onenextraitlafinqu’oncompareaveclachaîne"er".Lesdifférentespersonnesdelaconjugaisons’obtiennentenajoutantauverbe,privédesesdeuxdernierscaractères,l’unedesterminaisonsvouluesfourniesiciparuntableaudechaînesterminaisons.Onlesfaitprécéderd’unsujetextrait,luiaussi,d’untableaudechaînessujets.
publicclassConjug
{publicstaticvoidmain(Stringargs[])
{finalStringsujets[]=
{"je","tu","il/elle","nous","vous","ils/elles"};
finalStringterminaisons[]=
{"e","es","e","ons","ez","ent"};
Stringverbe;
183
intnbLettres;
System.out.print ("donnez un verbe regulier du premier groupe :
");
while(true)
{verbe=Clavier.lireString();
nbLettres=verbe.length();
Stringfin=verbe.substring(nbLettres-2,nbLettres);
if(fin.equals("er"))break;
System.out.print
("*** il ne se termine pas par er - donnez-en un autre :
");
}
Stringrad=verbe.substring(0,nbLettres-2);
intn=terminaisons.length;
for(inti=0;i<n;i++)
System.out.println(sujets[i]+""+rad+terminaisons[i]);
}
}
184
81 Synthèse:tridemots
Écrireunprogrammequilitunesuitedemotsauclavieraetquilesaffichetriésparordrealphabétique.Onsupposeraquecesmotsnecontiennentquedes lettresnonaccentuées(majusculesouminuscules).Lenombredemotsserafourniendonnéesetl’exécutionseprésenteraainsi:
Combiendemots?5
donnezvosmots
javaScript
Pascal
BaSiC
Java
ADA
Listeparordrealphabetique:
ADA
BaSiC
Java
javaScript
Pascal
Notezbienquelesmotssontaffichésavecleur"casse"d’originemaisquecelle-cin’influepassurletriquirespectel’ordrealphabétiquetraditionnel(quinedistinguepaslesmajusculesdesminuscules).
a.OnpourrautiliserlaclasseClavier(voirnoteendébutdechapitre).
Lesdifférenteschaînesconstituantlesmotssontluesdansuntableaud’objetsdetypeString dont la dimension nous est fournie en donnée. Pour en effectuer le tri, nousrecouronsàlaméthodesimpledu"triàbulle"quiconsisteàcomparerchaqueélémentà toussessuivants,enprocédantàunéchangechaquefoisqu’ilsnesontpasdans lebon ordre. On notera bien qu’ici, on peut se contenter de trier uniquement lesréférencesetnonpasleschaîneselles-mêmes,cequisefaittrèssimplementenJava.Pour les comparaisons de chaînes, nous pouvons recourir à la méthode compareTo.Cependant, celle-ci utilise comme ordre des caractères celui induit par la valeur deleurcode.Celasignifiequelesmajusculessontséparéesdesminuscules.Ilnousfaut
185
donc faire porter le tri sur les (références des) chaînes converties (par exemple) enmajuscules(àl’aidedelaméthodetoUpperCase).Maiscommel’énoncénousimposed’afficherlesmotstriéssuivantleurcassed’origine,nousdevonsconserveràlafoisletableauxdesréférencesdesmotstelsqu’ilsontétéfournisetuntableaudesréférencessurcesmêmesmotsconvertisenmajuscules.Deplus, lesdeux tableauxdoiventêtretriésenparrallèle.
publicclassTrisMots
{
publicstaticvoidmain(Stringargs[])
{//lecturedesdonnees
System.out.print("Combiendemots?");
intnMots=Clavier.lireInt();
String[]mots=newString[nMots];
System.out.println("donnezvosmots");
for(inti=0;i<nMots;i++)
mots[i]=Clavier.lireString();
//conversiondechaquemotenminuscules
String[]motsMin=newString[nMots];
for(inti=0;i<nMots;i++)
motsMin[i]=mots[i].toLowerCase();
// tri par reorganisation des references (mots d'origine et en
minuscules)
//(oncomparechaquemot(minuscule)atoussessuivants)
Stringtemp;
for(inti=0;i<nMots-1;i++)
for(intj=i+1;j<nMots;j++)
if(motsMin[i].compareTo(motsMin[j])>=0)
{ temp = motsMin[i] ; motsMin[i] = motsMin[j] ; motsMin[j] =
temp;
temp=mots[i];mots[i]=mots[j];mots[j]=temp;
}
//affichagedeschainestriees
System.out.println("Listeparordrealphabetique:");
for(inti=0;i<nMots;i++)//ou(depuisJDK5.0):
System.out.println(mots[i]);//for(Stringmot:mots)
}//System.out.println
(mot);
186
}
187
82 Synthèse:gestiond’unrépertoire
Réaliser une classe Repertoire permettant de gérer un répertoire téléphoniqueassociantunnumérodetéléphone(chaînedecaractères)àunnom.Pourfaciliterleschoses,onprévoirauneclasseAbonnedestinéeàreprésenterunabonnéetdisposantdesfonctionnalitésindispensables.LaclasseRepertoiredevradisposerdesfonctionnalitéssuivantes:
•constructeurrecevantunargumentdetypeentierprécisantlenombremaximumd’abonnésquepourracontenirlerépertoire(cetteparticularitéévited’avoiràsesoucierd’unegestiondynamiquedurépertoire),
•méthodeaddAbonne permettant d’ajouter un nouvel abonné ; elle renverra lavaleurfalsesilerépertoireestplein,lavaleurtruesinon,
•méthodegetNumerofournissantlenuméroassociéàunnomd’abonnéfournienargument,
• méthode getNAbonnes qui fournit le nombre d’abonnés figurant dans lerépertoire,
•méthodegetAbonnefournissantl’abonnédontlerangestfournienargument,
•méthodegetAbonnesTries fournissant un tableau des références des différentsabonnés, rangés par ordre alphabétique (pour simplifier, on supposera que lesnomssontécritsenminuscules,sanscaractèresaccentués).
Écrireunpetitprogrammedetest.
La classe Abonne ne présente pas de difficultés particulières. Si, comme il estconseillé, on y encapsule les champs de données, il faut simplement prévoir lesméthodesd’accèscorrespondantes:
classAbonne
{publicAbonne(Stringnom,Stringnumero)
{this.nom=nom;this.numero=numero;
}
publicStringgetNom(){returnnom;}
188
publicStringgetNumero(){returnnumero;}
privateStringnom,numero;
}
EncequiconcernelaclasseRepertoire,nouspouvonsnouspermettre,danslaméthodegetAbonne,defournirenrésultatunecopiedelaréférenceàl’abonnécorrespondant.En effet, ici l’objet correspondant n’est pas modifiable (champs privés, pas deméthodesd’altération).DanslaméthodegetAbonnesTries,nousfaisonsporter le trisurunecopiedutableaudes références aux différents abonnés, afin de ne pas modifier l’ordre initial durépertoire.VoicicequepourraitêtreladéfinitiondenotreclasseRepert:
classRepert
{publicRepert(intnMax)
{this.nMax=nMax;
rep=newAbonne[nMax];
nAbon=0;
}
publicbooleanaddAbonne(Abonnea)
{if(nAbon>=nMax)returnfalse;
rep[nAbon]=a;
nAbon++;
returntrue;
}
publicintgetNAbonnes(){returnnAbon;}
publicAbonnegetAbonne(intnum)
{if(num<nAbon)returnrep[num];
returnnull;
}
publicStringgetNumero(Stringnom)
{for(inti=0;i<=nAbon;i++)
if(nom.equals(rep[i].getNom()))returnrep[i].getNumero();
//ou(depuisJDK5.0):
//for(Abonnea:rep)
//if(nom.equals(a.getNom()))returna.getNumero();
returnnull;
}
publicAbonne[]getAbonnesTries()
189
{Abonne[]repTrie=newAbonne[nAbon];
for(inti=0;i<nAbon;i++)
repTrie[i]=rep[i];
for(inti=0;i<nAbon-1;i++)
for(intj=i+1;j<nAbon;j++)
if((repTrie[i].getNom()).compareTo(repTrie[j].getNom())>0)
{Abonnetemp=repTrie[i];
repTrie[i]=repTrie[j];
repTrie[j]=temp;
}
returnrepTrie;
}
privateintnMax,nAbon;
privateAbonne[]rep;
}
Voiciunpetitprogrammedetest,accompagnédesesrésultats:
publicclassTstRep
{publicstaticvoidmain(Stringargs[])
{Repertrep=newRepert(10);
System.out.println("ilya"+rep.getNAbonnes()+"abonnes");
Abonnea=newAbonne("Dupont","02-38-25-88-99");
Abonneb=newAbonne("Duval","04-55-66-77-99");
rep.addAbonne(a);
rep.addAbonne(b);
rep.addAbonne(newAbonne("Duchene","02-11-22-33-44"));
rep.addAbonne(newAbonne("Dubois","01-11-22-33-44"));
System.out.println("ilya"+rep.getNAbonnes()+"abonnes");
System.out.println("quisont:");
for(inti=0;i<rep.getNAbonnes();i++)
System.out.println(rep.getAbonne(i).getNom()+""
+rep.getAbonne(i).getNumero());
System.out.println("ouencore,parordrealphabetique");
Abonne[]abonnes=rep.getAbonnesTries();
for(inti=0;i<abonnes.length;i++)
System.out.println (abonnes[i].getNom() + " " +
abonnes[i].getNumero());
}
190
}
ilya0abonnes
ilya4abonnes
quisont:
Dupont02-38-25-88-99
Duval04-55-66-77-99
Duchene02-11-22-33-44
Dubois01-11-22-33-44
ouencore,parordrealphabetique
Dubois01-11-22-33-44
Duchene02-11-22-33-44
Dupont02-38-25-88-99
Duval04-55-66-77-99
1. Dans la méthode getAbonnesTries, nous avons pu nous contenter de recopierseulement les références des chaînes et non les chaînes elles-mêmes. En effet,l’utilisateur de cette méthode pourra toujours modifier les valeurs du tableau deréférences dont il reçoit la référence en retourmais il ne pourra pasmodifier leschaînesainsiréférencées.2.Dansunprogrammeréel,lesobjetsdetypeAbonnepourraientcomporterd’autresinformations(adresse…).Ilpourraitégalementêtrejudicieuxdevérifierlorsdelaconstructiond’untelobjetquelachaînecorrespondantaunumérorépondàcertainscritères.
1.Attentionànepasutiliserl’opérateur==quicomparerait,nonpaslesvaleursdeschaînes,maissimplementleursréférences!
191
Chapitre7
Lestypesénumérés
Connaissancesrequises
•Définitiond’untypeénumérésimple(sanschampsniméthodes)
•Utilisationdesvaleursd’untypeénuméré
•Comparaisonsd’égalitéentrevaleursd’untypeénuméré:opérateur==ouméthodeequals
•Ordredesvaleursd’untypeénuméré:méthodescompareToetordinal
•Conversionenchaînesdesconstantesd’untypeénuméré,aveclaméthodetoString
•Conversionéventuelled’unechaîneenunevaleurd’untypeénuméré;méthodevalueOf
•MéthodevaluesdelaclasseEnum
•Itérationsurlesconstantesd’untypeénuméré
•Introductiondechampsetdeméthodesdansuntypeénuméré;casparticulierdesconstructeurs(transmissiond’arguments)
Note:Lestypesénumérésnesontdisponiblesqu’àpartirduJDK5.0.
192
83 Définitionetutilisationd’untypeénumérésimple
1.DéfiniruntypeénumérénomméCouleursdontlesvaleurssontdéfiniesparlesidentificateurssuivants:rouge,bleu,vert,jaune.2.Déclarerdeuxvariablesc1etc2dutypeCouleursetleuraffecterunevaleur.3.Échangerlecontenudecesdeuxvariables,ens’assurantaupréalablequeleursvaleursnesontpaségales.4.Regroupertoutescesinstructionsdansunepetitprogrammecomplet(onpourraajouter des instructions d’affichage des valeurs des variables avant et aprèséchange).
1.Ladéfinitiond’untypeénuméréenJavautiliseunesyntaxedelaforme:enumNomType{valeur1,valeur2,…valeurN}soit,ici:enumCouleurs{rouge,bleu,vert,jaune}
Notez que, bien que l’on emploie le mot-clé enum et non class,Couleurs est àconsidérer comme un classe particulière. Les valeurs du type (rouge,bleu, vert etjaune)ensontdesinstancesfinales(nonmodifiables).2.LadéclarationdevariablesdutypeCouleursestclassique:Couleursc1,c2;
On ne peut affecter à ces variables que des valeurs du typeCouleurs. Ici, il peuts’agirde l’unedes4constantesdu type :on lesnommeen lespréfixantdunomdetype(iciCouleurs)commedans:c1=Couleurs.bleu;//attention:c1=bleuseraiterroné
c2=Couleurs.jaune;
3.Lacomparaisondedeuxvariablesdetypeénumérépeutsefaire indifféremmentavec l’un des opérateurs == ou equals. Rappelons que le premier compare lesréférencesdesobjetscorrespondants,tandisquelesecondportesurlesvaleursdecesobjets.Mais,commeiln’existequ’unexemplairedechaqueobjet représentant
193
unconstanted’untypeénuméré,ilrevientbienaumêmedecomparerleurréférenceouleurvaleur.Demême,onpeututiliserindifféremment!=ou!equals.if(c1!=c2)//ouif(!c1.equals(c2))
{Couleursc;
c=c1;
c1=c2;
c2=c;
}
4.Voiciunexemplecompletreprenantcesdifférentesinstructions,accompagnéd’unexemple d’exécution.On notera qu’il est très facile d’afficher une valeur de typeénumérépuisquel’appelimpliciteàlaméthode toStringpourune instancede typeénuméréfournitsimplementlelibellécorrespondant:publicclassEnumSimple
{publicstaticvoidmain(Stringargs[])
{Couleursc1,c2;
c1=Couleurs.bleu;//attention:c1=bleuseraiterroné
c2=Couleurs.jaune;
System.out.println ("couleurs avant echange = " + c1 + " " +
c2);
if(c1!=c2)//ouif(!c1.equals(c2))
{Couleursc;
c=c1;
c1=c2;
c2=c;
}
System.out.println ("couleurs apres echange = " + c1 + " " +
c2);
}
}
enumCouleurs{rouge,bleu,vert,jaune}
couleursavantechange=bleujaune
couleursapresechange=jaunebleu
194
84 Itérationsurlesvaleursd’untypeénuméré
Onsupposequ’ondisposed’un typeénumérénomméSuite. Écrire un programmequienaffichelesdifférentslibellés.Parexemple,siSuiteaétédéfiniainsi(notezl’emploidulibelléut,cardon’estpasutilisablepuisqu’ils’agitd’unmot-clé):
enumSuite{ut,re,mi,fa,sol,la,si}
Leprogrammeaffichera:ListedesvaleursdutypeSuite:
ut
re
mi
fa
sol
la
si
Onpeut facilement itérersur lesdifférentesvaleursd’un typeénuméréà l’aidede laboucleditefor…each,introduiteparleJDK5.0.Ilfautcependantaupréalablecréerun tableau des valeurs du type en utilisant la méthode values de la classe Enum ;l’expressionSuite.values()représenteuntableauformédesdifférentesvaleursdutypeSuite. En définitive, voici le programme voulu ; il fonctionne quelle que soit ladéfinitiondutypeSuite:publicclassTstSuite
{publicstaticvoidmain(Stringargs[])
{System.out.println("ListedesvaleursdutypeSuite:");
for(Suites:Suite.values())
System.out.println(s);//appelimplicitedetoString()
}
}
enumSuite{ut,re,mi,fa,sol,la,si}
195
85 Accèsparleurrangauxvaleursd’untypeénuméré(1)
Onsupposequ’ondisposed’un typeénumérénomméSuite. Ecrire un programmequi:
•affichelenombredevaleursdutype,
•affichelesvaleursderangimpair,
•afficheladernièrevaleurdutype.
Unedémarche simple consiste à créerun tableaudesvaleursdu type, à l’aidede laméthode values de la classe Enum. Il suffit ensuite d’exploiter classiquement cetableaupourobtenirlesinformationsvoulues:publicclassTstValues
{publicstaticvoidmain(Stringargs[])
{//Oncréeuntableaudesvaleursdutype,àl'aidedelaméthode
values
Suite[]valeurs=Suite.values();
intnbVal=valeurs.length;
System.out.println("letypeSuitecomporte"+nbVal+"valeurs"
);
System.out.println("valeursderangimpair=");
for(inti=0;i<nbVal;i+=2)
System.out.println(valeurs[i]);
System.out.println("dernierevaleurdutype:");
System.out.println(valeurs[nbVal-1]);
}
enumSuite{ut,re,mi,fa,sol,la,si}
letypeSuitecomporte7valeurs
valeursderangimpair=
ut
mi
196
sol
si
dernierevaleurdutype:
si
Onnotera que le programmen’est pas protégé contre le risque que le typeSuite necomporteaucunélément.
197
86 Lecturedevaleursd’untypeénuméré
Onsupposequ’ondisposed’un typeénumérénomméSuite. Écrire un programmequi litunechaîneauclavieretqui indiquesicettechaînecorrespondounonàunlibellédutypeetqui,lecaséchéant,enaffichelerangdanslesvaleursdutype.
A priori, toute classe d’énumération dispose d’uneméthode valueOf qui effectue laconversioninversede toString,àsavoir :convertirunechaîneenunevaleurdutypeénuméré correspondant. Cependant, si la chaîne en question ne correspond à aucunevaleurdutype,onaboutitàuneexceptionquidoitalorsêtreinterceptée,souspeinedevoir le programme s’interrompre. Ici, nous vous proposons une démarche, moinsdirecte,maisnecomportantplusde risqued’exception, à savoir : parcourir chacunedes valeurs du type énuméré (à l’aide du tableau fourni par la méthode values) encomparantsaconversionenchaîne(toString)aveclachaînefournieauclavier.
publicclassLectureEnum
{publicstaticvoidmain(Stringargs[])
{StringchSuite;
System.out.print("Donnezunlibelledel'enumerationSuite:");
chSuite=Clavier.lireString();
booleantrouve=false;
for(Suitej:Suite.values())
{if(chSuite.equals(j.toString()))
{trouve=true;
intnumSuite=j.ordinal();
System.out.println(chSuite+"correspondalavaleurderang"
+(numSuite+1)+"dutypeSuite");
}
}
if(!trouve)System.out.println(chSuite
+"n'appartientpasautypeSuite");
}
198
}
enumSuite{ut,re,mi,fa,sol,la,si}
Donnezunlibelledel'enumerationSuite:Re
Ren'appartientpasautypeSuite
Donnezunlibelledel'enumerationSuite:mi
micorrespondalavaleurderang3dutypeSuite
199
87 Ajoutdeméthodesetdechampsàuneénumération(1)
DéfiniruntypeénumérénomméMoispermettantdereprésenter lesdouzemoisdel’année, en utilisant les noms usuels (janvier, fevrier,mars…) et en associant àchacun le nombre de jours correspondants.On ne tiendra pas compte des annéesbisextiles.Écrire un petit programme affichant ces différents noms avec le nombre de jourscorrespondantscommedans:
janviercomporte31jours
fevriercomporte28jours
marscomporte31jours
.....
octobrecomporte31jours
novembrecomporte30jours
decembrecomporte31jours
Javavouspermetdedoteruntypeénumérationdechampsetdeméthodes,commes’ils’agissaitd’uneclasse.Certainesdecesméthodespeuventêtredesconstructeurs;dansce cas, il est nécessaire d’utiliser une syntaxe spéciale dans la définition du typeénuméré pour fournir les arguments destinés au constructeur, en association avec lelibellécorrespondant.VoicicommentnouspourrionsdéfinirnotretypeMois,enlemunissant:
•d’unchampnjdestinéàcontenirlenombredejoursd’unmoisdonné,
•d’unconstructeurrecevantenargumentlenombredejoursdumois,
•d’uneméthodenbJoursfournissantlenombredejoursassociéàunevaleurdonnée.enumMois
{janvier(31),fevrier(28),mars(31),avril(30),
mai(31),juin(30),juillet(31),aout(31),
septembre(30),octobre(31),novembre(30),decembre(31);
privateMois(intn)//constructeur(enargument,nombredejours
200
dumois)
{nj=n;;
}
publicintnbJours(){returnnj;}
privateintnj;
}
Notezlesparticularitésdelasyntaxe:
•présenced’argumentspourleconstructeur,
• présence d’un point-virgule séparant l’énumération des valeurs du type desdéclarationsdeschampsetméthodes.
Voiciunpetitprogrammefournissantlalistevoulue.publicclassTstMois
{publicstaticvoidmain(Stringargs[])
{for(Moism:Mois.values())
System.out.println ( m + " comporte " + m.nbJours() + "
jours");
}
}
201
88 Ajoutdeméthodesetdechampsàuneénumération(2)
CompléterlaclasseMoisprécédente,demanièreàassocieràchaquenomdemois:
•unnombredejours,
•uneabréviationdetroiscaractères(jan,fev…),
•lenomanglaiscorrespondant.Écrire un petit programme affichant ces différentes informations sous la formesuivante:
jan=janvier=january-31jours
fev=fevrier=february-28jours
mar=mars=march-31jours
.....
oct=octobre=october-31jours
nov=novembre=november-30jours
dec=decembre=december-31jours
Ilsuffitd’adapterl’énumérationMoisdel’exerciceprécédentdelafaçonsuivante:
• introduction de nouveaux champs abrege et anglais pour y conserver lesinformationsrelativesaunomabrégéetaunomanglais,
• ajout de méthodes abreviation et nomAnglais fournissant chacune de cesinformations,
•adaptationduconstructeurpourqu’ildisposecettefoisdetroisarguments.enumMois2
{janvier(31,"jan","january"),fevrier(28,"fev","february"),
mars(31,"mar","march"),avril(30,"avr","april"),
mai(31,"mai","may"),juin(30,"jun","june"),
juillet(31,"jul","july"),aout(31,"aou","august"),
septembre(30,"sep","september"),octobre(31,"oct","october"),
202
novembre (30, "nov", "november"), decembre (31, "dec",
"december");
privateMois2(intn,Stringabrev,Stringna)
{nj=n;
abrege=abrev;
anglais=na;
}
publicintnbJours(){returnnj;}
publicStringabreviation()
{returnabrege;
}
publicStringnomAnglais()
{returnanglais;
}
privateintnj;
privateStringabrege;
privateStringanglais;
}
publicclassTstMois2
{publicstaticvoidmain(Stringargs[])
{for(Mois2m:Mois2.values())
System.out.println(m.abreviation()+"="+m+"="
+m.nomAnglais()+"-"+m.nbJours()+"jours");
}
}
203
89 Synthèse:gestionderésultatsd’examens
On se propose d’établir les résultats d’examen d’un ensemble d’élèves. Chaqueélève sera représenté par un objet de typeEleve, comportant obligatoirement leschampssuivants:
•lenomdel’élève(typeString),
• son admissibilité à l’examen, sous forme d’une valeur d’un type énumérécomportantlesvaleurssuivantes:N(nonadmis),P(passable),AB(Assezbien),B(Bien),TB(Trèsbien).
Idéalement, les noms des élèves pourraient être contenus dans un fichier. Ici, parsoucide simplicité, nous les supposerons fournisparun tableaude chaînesplacédansleprogrammeprincipal.Ondemandededéfinir convenablement la classeEleve et d’écrire unprogrammeprincipalqui:
• pour chaque élève, lit au clavier 3 notes d’examen, en calcule lamoyenne etrenseigneconvenablementlechampd’admissibilité,suivantlesrèglesusuelles:–moyenne<10:Nonadmis–10<=moyenne<12:Passable–12<=moyenne<14:Assezbien–14<=moyenne<16:Bien–16<=moyenne:Trèsbien
•affichel’ensembledesrésultatsenfournissantenclairlamentionobtenue.Voiciunexempled’exécutiond’untelprogramme:donnezlestroisnotesdel'eleveDutronc
11.5
14.5
10
donnezlestroisnotesdel'eleveDunoyer
9.5
10.5
204
9
donnezlestroisnotesdel'eleveLechene
14.5
12
16.5
donnezlestroisnotesdel'eleveDubois
6
14
11
donnezlestroisnotesdel'eleveFrenet
17.5
14
18.5
Resultats:
Dutronc-Assezbien
Dunoyer-Nonadmis
Lechene-Bien
Dubois-Passable
Frenet-Tresbien
L’énoncénous impose ladéfinitiondu typeénuméré contenant lesdifférents résultatspossiblesdel’examen.Onnoteraqu’onnousdemanded’affichercesrésultatssousuneforme«longue»,parexemplePassableetnonsimplementP.Nousassocieronsdoncun texte à chacunedesvaleursdenotre typeénuméré, enexploitant lapossibilitédedoterunteltypedeméthodes,àsavoirici:
•unconstructeurrecevantenargumentletexteassociéàlavaleur,
•uneméhtodenomméedetails,permettantdetrouvercetexteàpartird’unevaleur.Voicicequepourraitêtreladéfinitiondecetypeénuméré:enumMention
{NA("Nonadmis"),P("Passable"),AB("Assezbien"),
B("Bien"),TB("Tresbien"),NC("Nonconnu");
privateMention(Stringd)
{mentionDetaillee=d;
}
publicStringdetails()
{returnmentionDetaillee;
205
}
privateStringmentionDetaillee;
}
Un champ privé nommémentionDetaillee nous sert à conserver le texte associé àchaquevaleur.Notezque,pourdesquestionsdesécurité,nousavonsprévuunevaleursupplémentaire(NC)correspondantàunrésultatnonconnu,aveclaquellesetrouveraautomatiquementinitialisée(parleconstructeur)toutevariabledutypeMention,Nousavonsprévud’utiliserdeuxméthodesstatiques:
•doublemoyenne(Stringn)quidemandedefournirtroisnotespourlenomnetquiencalculelamoyenne,
•Mention resul (double m) qui fournit la mention correspondant à une moyennedonnéem.
Voicicequepourraitêtreleprogrammedemandé:publicclassExamen
{publicstaticvoidmain(Stringargs[])
{ String noms[] = { "Dutronc", "Dunoyer", "Lechene", "Dubois",
"Frenet"};
//creationdutableaud'eleves
intnel=noms.length;
Eleveeleves[]=newEleve[nel];
for(inti=0;i<nel;i++)
eleves[i]=newEleve(noms[i]);
//lecturedesnotesetdéterminationdurésultatdechaqueélève
for(Eleveel:eleves)
{doublemoy=moyenne(el.getNom());
el.setResul((resul(moy)));
}
//affichagerésultats
System.out.println("Resultats:");
for(Eleveel:eleves)
System.out.println (el.getNom() + " - " +
el.getResul().details());
}
//méthodequidemandeauclaviertroisnotespourunnomdonne
//etquifournitenretourlamoyennecorrespondante
206
staticpublicdoublemoyenne(Stringn)
{System.out.println("donnezlestroisnotesdel'eleve"+n);
doublesom=0.;
for(inti=0;i<3;i++)
{doublenote=Clavier.lireDouble();
som+=note;
}
doublemoyenne=som/3.;
returnmoyenne;
}
//méthodequidéfinitlamentionenfonctiondelamoyenne
staticpublicMentionresul(doublem)
{if(m<10.)returnMention.NA;
if(m<12.0)returnMention.P;
if(m<14.0)returnMention.AB;
if(m<16.0)returnMention.B;
returnMention.TB;
}
}
classEleve
{publicEleve(Stringn)
{nom=n;
resul=Mention.NC;//valeurpardéfaut
}
publicvoidsetResul(Mentionr)
{resul=r;
}
publicMentiongetResul()
{returnresul;
}
publicStringgetNom()
{returnnom;
}
privateStringnom;
privateMentionresul;
}
enumMention
207
{NA("Nonadmis"),P("Passable"),AB("Assezbien"),
B("Bien"),TB("Tresbien"),NC("Nonconnu");
privateMention(Stringd)
{mentionDetaillee=d;
}
publicStringdetails()
{returnmentionDetaillee;
}
privateStringmentionDetaillee;
}
208
Chapitre8
Lesexceptions
Connaissancesrequises
•Déclenchementd’uneexceptionavecthrow
•Bloctry,écritured’ungestionnaired’exception
•Transmissiond’informationsaugestionnaired’exception
•Règlesdechoixdugestionnaired’exception
•Cheminementd’uneexception
•Clausethrows
•Blocfinally
•Redéclenchementd’uneexception
•Exceptionsstandard
209
90 Déclenchementettraitementd’uneexception
RéaliseruneclasseEntNatpermettantdemanipulerdesentiersnaturels(positifsounuls).Pourl’instant,cetteclassedisposerasimplement:
•d’unconstructeuràunargumentdetypeintquigénérerauneexceptiondetypeErrConst(typeclasseàdéfinir)lorsquelavaleurreçueneconviendrapas,
•d’uneméthodegetNfournissantsousformed’unint, lavaleurencapsuléedansunobjetdetypeEntNat.
Écrireunpetitprogrammed’utilisationquitraitel’exceptionErrConstenaffichantunmessageeteninterrompantl’exécution.
Le constructeur de la classe EntNat doit donc déclencher une exception de typeErrConst lorsque la valeur reçue par son constructeur est négative. Ici, la classeErrConst peut être réduite à sa plus simple expression, à savoir ne comporter nichampsniméthodes.LadéfinitiondeEntNatpourraitseprésenterainsi:
classEntNat
{publicEntNat(intn)throwsErrConst
{if(n<0)thrownewErrConst();
this.n=n;
}
publicintgetN(){returnn;}
privateintn;
}
classErrConstextendsException
{}
Onnoteraqu’enl’absencedelaclausethrowsErrConstdansl’en-têteduconstructeurdeEntNat,onobtiendraituneerreurdecompilation.D’autrepart,ilestindispensablequelaclasseErrConstdérivedelaclasseException(lecompilateurs’assurebienquel’objetmentionnéàthrowestd’untypecompatibleavecException).
210
Voici un programme d’utilisation dans lequel nous traitons l’exceptionErrConst enincluant les instructions concernées dans un bloc try que nous faisons suivre d’ungestionnaire introduit par catch(ErrConst e). Comme demandé, nous y affichons unmessage(***erreurconstruction***)etnousmettonsfinàl’exécutionparl’appeldeSystem.exit.
publicclassTstEntNat
{publicstaticvoidmain(Stringargs[])
{try
{EntNatn1=newEntNat(20);
System.out.println("n1="+n1.getN());
EntNatn2=newEntNat(-12);
System.out.println("n2="+n2.getN());
}
catch(ErrConste)
{System.out.println("***erreurconstruction***");
System.exit(-1);
}
}
}
n1=20
***erreurconstruction***
211
91 Transmissiond’informationaugestionnaire
AdapterlaclasseEntNatdel’exerciceetleprogrammed’utilisationdemanièreàdisposerdans legestionnaired’exceptiondu typeErrConstde lavaleur fournieàtortauconstructeur.
Cette fois, nous prévoyons, dans la classe ErrConst, un champ valeur destiné àconserver lavaleuravec laquelleona tentédeconstruireà tortunentiernaturel.Lafaçon laplus simpled’attribuerunevaleur à ce champconsiste à le faire lorsde lacréationdel’objetdetypeErrConst,enlatransmettantauconstructeur.Ici,nousavonsfaitdevaleurunchampprivé,desortequenousdotonsnotreclasseErrConstd’uneméthode d’accès getValeur. Voici la nouvelle définition de nos classes EntNat etErrConst:
classEntNat
{publicEntNat(intn)throwsErrConst
{if(n<0)thrownewErrConst(n);
this.n=n;
}
publicintgetN(){returnn;}
privateintn;
}
classErrConstextendsException
{publicErrConst(intvaleur){this.valeur=valeur;}
publicintgetValeur(){returnvaleur;}
privateintvaleur;
}
Dansnotreprogrammed’utilisation,nousdevonsrécupérerlavaleurcoupabledanslegestionnaired’exception.IlnoussuffitpourceladerecouriràlaméthodegetValeur:
publicclassTstEntN1
212
{publicstaticvoidmain(Stringargs[])
{try
{EntNatn1=newEntNat(20);
System.out.println("n1="+n1.getN());
EntNatn2=newEntNat(-12);
System.out.println("n2="+n2.getN());
}
catch(ErrConste)
{System.out.println("***tentativeconstructionnaturelavec"
+e.getValeur()+"***");
System.exit(-1);
}
}
}
n1=20
***tentativeconstructionnaturelavec-12***
Enpratique,onsepermettrasouventdenepasappliquerleprinciped’encapsulationàdeschamps telsquevaleur.Ainsi, en ledéclarantpublic, onpourra se passer de laméthodegetValeuretécriredirectementdanslegestionnaire:
System.out.println("***tentativeconstructionnaturelavec"
+e.valeur+"***");
213
92 Cheminementdesexceptions
Queproduitleprogrammesuivantlorsqu’onluifournitendonnéea:
•lavaleur0,
•lavaleur1,
•lavaleur2.classExceptextendsException
{publicExcept(intn){this.n=n;}
publicintn;
}
publicclassChemin
{publicstaticvoidmain(Stringargs[])
{intn;
System.out.print ("donnez un entier : ") ; n =
Clavier.lireInt();
try
{System.out.println("debutpremierbloctry");
if(n!=0)thrownewExcept(n);
System.out.println("finpremierbloctry");
}
catch(Excepte)
{System.out.println("catch1-n="+e.n);
}
System.out.println("suiteduprogramme");
try
{System.out.println("debutsecondbloctry");
if(n!=1)thrownewExcept(n);
System.out.println("finsecondbloctry");
}
catch(Excepte)
{ System.out.println ("catch 2 - n = " + e.n) ;
System.exit(-1);
}
System.out.println("finprogramme");
214
}
}
a.Pourlireunentierauclavier,ilutiliselaméthodelireIntdelaclasseClavierfourniesurlesiteWeb.
Ici,ilfautsimplementsavoirquelorsquelegestionnaired’exceptionnecomportepasd’arrêtdel’exécution(oud’instructionreturn), l’exécutionsepoursuità l’instructionsuivantlederniergestionnaireassociéaubloctry.Endéfinitive,voiciquelsserontlestroisexemplesd’exécutioncorrespondantauxtroisréponsesproposées:
donnezunentier:0
debutpremierbloctry
finpremierbloctry
suiteduprogramme
debutsecondbloctry
catch2-n=0
donnezunentier:1
debutpremierbloctry
catch1-n=1
suiteduprogramme
debutsecondbloctry
finsecondbloctry
finprogramme
donnezunentier:2
debutpremierbloctry
catch1-n=2
suiteduprogramme
debutsecondbloctry
catch2-n=2
215
93 Cheminementdesexceptionsetchoixdugestionnaire
Quelsrésultatsfournitceprogramme?classErreurextendsException
{publicintnum;
}
classErreur_dextendsErreur
{publicintcode;
}
classA
{publicA(intn)throwsErreur_d
{ if (n==1) { Erreur_d e = new Erreur_d() ; e.num = 999 ;
e.code=12;
throwe;
}
}
}
publicclassChemin1
{
publicstaticvoidmain(Stringargs[])
{try
{Aa=newA(1);
System.out.println("aprescreationa(1)");
}
catch(Erreure)
{System.out.println("**exceptionErreur"+e.num);
}
System.out.println("suitemain");
try
{Ab=newA(1);
System.out.println("aprescreationb(1)");
}
catch(Erreur_de)
216
{System.out.println("**exceptionErreur_d"+e.num+""+
e.code);
}
catch(Erreure)
{System.out.println("**exceptionErreur"+e.num);
}
}
}
Quesepasse-t-ilsil’oninversel’ordredesdeuxgestionnairesdanslesecondbloctry?
Danslepremierbloctry,l’appelduconstructeurdeAdéclencheuneexceptiondetypeErreur_d.Celle-ci est traitéepar l’uniquegestionnaire relatif au typeErreur, lequelconvienteffectivementpuisqueErreur_ddérivedeErreur.Danslesecondbloctry,ondéclenchelamêmeexceptionmais,cettefois,deuxgestionnairesluisontassociés.Lepremiertrouvéconvientetc’estdoncluiquiestexécuté.Endéfinitive,onobtientlesrésultatssuivants:**exceptionErreur999
suitemain
**exceptionErreur_d99912
Notezbienqu’ici,lesmessagesaprescreation…nesontpasaffichéspuisquelesdeuxblocstrysontinterrompusauparavant.Sil’oninversel’ordredesdeuxgestionnairesdanslesecondbloctry,onobtientuneerreurdecompilationcarlesecondn’aaucunechanced’êtresélectionné.
217
94 Cheminementdesexceptions
Quefaitceprogrammea?classPositif
{publicPositif(intn)throwsErrConst
{if(n<=0)thrownewErrConst();
}
}
classErrConstextendsException
{}
publicclassTstPos
{publicstaticvoidmain(Stringargs[])
{System.out.println("debutmain");
booleanok=false;
while(!ok)
{try
{System.out.print("donnezunentierpositif:");
intn=Clavier.lireInt();
Positifep=newPositif(n);
ok=true;
}
catch(ErrConste)
{System.out.println("***erreurconstruction***");
}
}
System.out.println("finmain");
}
}
a.Pourlireunentierauclavier,ilutiliselaméthodelireIntdelaclasseClavierfourniesurlesiteWeb.
Dans labouclewhilede laméthodemain,on litunnombreentierqu’on transmetauconstructeur de Positif. Si la valeur qu’il reçoit n’est pas strictement positive, ildéclenche une exception de typeErrConst. Celle-ci est traitée dans le gestionnaire
218
associéaubloctry,lequelsecontented’afficherunmessage(***erreurconstruction***).Aprèsl’exécutiondecegestionnaire,onpasseàl’instructionsuivantlebloctry,c’est-à-direiciautestdepoursuitedelabouclewhile,baséesurlavaleurdeok.Onconstate que la boucle se poursuit jusqu’à ce que la valeur de n soit effectivementpositive.Dansce cas, eneffet, la constructiondeep sedéroulenormalement et l’onexécutel’instructionok=true.Voici un exemple d’exécution de ce programme, dans lequel on déclenche à deuxreprisesl’exceptionErrConst:
debutmain
donnezunentierpositif:-5
***erreurconstruction***
donnezunentierpositif:0
***erreurconstruction***
donnezunentierpositif:4
finmain
Voiciunautreexempledanslequelaucuneexceptionn’aétédéclenchée:
debutmain
donnezunentierpositif:5
finmain
Engénéral, iln’estpasconseilléd’employerlemécanismedegestiondesexceptionspourcontrôlerledéroulementd’unebouclecommenouslefaisonsici.Cependant,cettedémarchepourras’avérerutiledansquelquesrarescirconstances,notammentpourlireunfichierséquentiel;danscecas,onsebaserasurl’exceptionEOFException.
219
95 Instructionreturndansungestionnaire
Quefournitleprogrammesuivant?classErreurextendsException
{}
classA
{publicA(intn)throwsErreur
{if(n==1)thrownewErreur();
}
}
publicclassChemin2
{publicstaticvoidmain(Stringargs[])
{f(true);System.out.println("apresf(true)");
f(false);System.out.println("apresf(false)");
}
publicstaticvoidf(booleanret)
{try
{Aa=newA(1);
}
catch(Erreure)
{System.out.println("**Dansf-exceptionErreur");
if(ret)return;
}
System.out.println("suitef");
}
}
Lesdeuxappelsdefdéclenchentuneexceptionàlaconstructiondea.Toutefois,danslepremiercas,legestionnaireestamenéàexécuteruneinstructionreturn,cequimetfinàl’exécutiondef,sansquel’instructionsuivantlebloctry(affichagedesuitef)nesoit exécutée. En revanche, elle l’est bien dans le second cas où le gestionnaire se
220
terminenaturellement,sansqu’aucuneinstructionreturnouexitn’aitétéexécutée.Endéfinitive,leprogrammefournitlesrésultatssuivants:
**Dansf-exceptionErreur
apresf(true)
**Dansf-exceptionErreur
suitef
apresf(false)
221
96 Redéclenchementd’uneexceptionetchoixdugestionnaire
Quefournitceprogramme?classErreurextendsException{}
classErreur1extendsErreur{}
classErreur2extendsErreur{}
classA
{publicA(intn)throwsErreur
{try
{if(n==1)thrownewErreur1();
if(n==2)thrownewErreur2();
if(n==3)thrownewErreur();
}
catch(Erreur1e)
{ System.out.println ("** Exception Erreur1 dans constructeur
A");
}
catch(Erreure)
{ System.out.println ("** Exception Erreur dans constructeur
A");
throw(e);
}
}
}
publicclassRedecl
{publicstaticvoidmain(Stringargs[])
{intn;
for(n=1;n<=3;n++)
{try
{Aa=newA(n);
}
catch(Erreur1e)
{System.out.println("***ExceptionErreur1dansmain");
222
}
catch(Erreur2e)
{System.out.println("***ExceptionErreur2dansmain");
}
catch(Erreure)
{System.out.println("***ExceptionErreurdansmain");
}
System.out.println("--------------");
}
System.out.println("finmain");
}
}
Ici,onexécuteàtroisrepriseslemêmebloctry,nprenantsuccessivementlesvaleurs1,2et3.Danslepremiercas(n=1),laconstructiondel’objetadéclencheuneerreurErreur1qui se trouve traitée par le gestionnaire correspondant (catch(Erreur1 e) duconstructeurdeA.Danslesecondcas(n=2),laconstructiondeadéclencheuneerreurErreur2.ElleestalorstraitéeparlegestionnairerelatifautypeErreurduconstructeurdeA(ils’agitdupremier des gestionnaires ayant un type compatible avec Erreur2). Mais celui-ciredéclenche à son tour une exception demême typeErreur2 ; transmise au bloc tryenglogant,elleesttraitéeparlegestionnairecatch(Erreur2).Enfin,danslederniercas(n=3),laconstructiondeadéclencheuneerreurErreurquise trouve traitée par le gestionnaire correspondant du constructeur de A lequel, làencore, redéclenche à son tour une exception demême type ; transmise au bloc tryenglobant,elleseratraitéeparlegestionnairecatch(Erreur).Endéfinitive,leprogrammefournitcesrésultats:**ExceptionErreur1dansconstructeurA
--------------
**ExceptionErreurdansconstructeurA
***ExceptionErreur2dansmain
--------------
**ExceptionErreurdansconstructeurA
***ExceptionErreurdansmain
--------------
223
finmain
Notez bien qu’ici le type de l’exception redéclenchée par le second gestionnaire duconstructeurdeA (instruction throwe)est identiqueàcelui reçuenargument. Ilpeutvarierd’unappelàunautre.Ici,ils’agitsoitdeErreur2,soitdeErreur.
224
97 Blocfinally
Quelsrésultatsfournitceprogramme?classExceptextendsException
{}
publicclassFinally
{publicstaticvoidf(intn)
{try
{if(n!=1)thrownewExcept();
}
catch(Excepte)
{System.out.println("catchdansf-n="+n);
return;
}
finally
{System.out.println("dansfinally-n="+n);
}
}
publicstaticvoidmain(Stringargs[])
{f(1);
f(2);
}
}
Lesinstructionsd’unflocfinallyassociéàunbloctrysonttoujoursexécutéesqu’ilyait eu ou non déclenchement d’une exception (sauf si le gestionnaire met fin àl’exécution). Ceci s’applique notamment au cas où un gestionnaire comporte uneinstructionreturn:leblocfinallyestquandmêmeexécutéauparavant.Endéfinitive,leprogrammefournitceci:
dansfinally-n=1
catchdansf-n=2
dansfinally-n=2
225
98 Redéclenchementetfinally
Quelsrésultatsfournitceprogramme?classExceptextendsException{}
publicclassFinReth
{publicstaticvoidf(intn)throwsExcept
{try
{if(n!=1)thrownewExcept();
}
catch(Excepte)
{System.out.println("catchdansf-n="+n);
throwe;
}
finally
{System.out.println("dansfinallydef-n="+n);
}
}
publicstaticvoidmain(Stringargs[])
{intn=0;
try
{for(n=1;n<5;n++)f(n);
}
catch(Excepte)
{System.out.println("catchdansmain-n="+n);
}
finally
{System.out.println("dansfinallydemain-n="+n);
}
}
}
Labouclefordelaméthodemaineffectuethéoriquementcinqappelsdef.Lepremier(n=1)neprovoqueaucuneexceptiondansf,etilconduitàl’exécutiondublocfinally
226
associé au bloc try de f. Le deuxième (n=2) provoque une exception dans f qui esttraitéeparlebloccatchcorrespondant,lequelrelanceànouveauuneexception;avantqu’on ne lui cherche un gestionnaire, on exécute le bloc finally associé au bloc try.Puisonchercheungestionnaireappropriédansunbloctryenglobant,c’est-à-direicicelui dumain. On exécute donc le bloc catch correspondant, puis le bloc finallyassocié. Comme cette exception met fin à l’exécution du bloc try de main, leprogrammes’interrompt.Endéfinitive,onobtientcesrésultats:
dansfinallydef-n=1
catchdansf-n=2
dansfinallydef-n=2
catchdansmain-n=2
dansfinallydemain-n=2
227
99 Synthèse:entiersnaturels
Réaliseruneclassepermettantdemanipulerdesentiersnaturels(positifsounuls)etdisposant:
•d’unconstructeuràunargumentdetypeint;ilgénérerauneexceptionErrConstsilavaleurdesonargumentestnégative;
•deméthodesstatiquesdesomme,dedifférenceetdeproduitdedeuxnaturels;elles généreront respectivement des exceptions ErrSom, ErrDiff et ErrProdlorsque le résultatneserapas représentable ; la limitedesvaleursdesnaturelsserafixéeàlaplusgrandevaleurdutypeint;
•uneméthoded’accèsgetNfournissantsousformed’unint lavaleurdel’entiernaturel.
Ons’arrangerapourquetouteslesclassesexceptiondériventd’uneclasseErrNatetpourqu’ellespermettent àunéventuelgestionnairede récupérer lesvaleursayantprovoquél’exception.Écriredeuxexemplesd’utilisationdelaclasse:
•l’unsecontentantd’interceptersansdiscernementlesexceptionsdetypedérivédeErrNat,
• l’autre qui explicite la nature de l’exception en affichant les informationsdisponibles.
Lesdeuxexemplespourrontfigurerdansdeuxblocstryd’unmêmeprogramme.
L’énoncénousimposederespecterunecertainehiérarchiepourlesclassesexception.Ici, pour faciliter la tâche, nous prévoyons une classe intermédiaire supplémentairenomméeErrOpquiserviradebaseauxexceptionsliéesàdesopérationsarithmétiques(somme,différenceouproduit);ellepossèderatoutnaturellementdeuxchampsdetypeint(onauraitpuchoisiraussiEntNat)représentantlesvaleursdesdeuxopérandesdel’opération.Lahiérarchiedesclassesd’exceptionseprésenteradoncainsi:Exception
228
ErrNatErrConstErrOp
ErrSomErrDifErrProd
VoiciladéfinitiondenotreclasseEntNatetdesclassesexceptioncorrespondantes:
classEntNat
{publicEntNat(intn)throwsErrConst
{if(n<0)thrownewErrConst(n);
this.n=n;
}
public static EntNat somme (EntNat n1, EntNat n2) throws ErrSom,
ErrConst
{intop1=n1.n,op2=n2.n;
longs=op1+op2;
if(s>Integer.MAX_VALUE)thrownewErrSom(op1,op2);
returnnewEntNat(op1+op2);
}
public static EntNat diff (EntNat n1, EntNat n2) throws ErrDiff,
ErrConst
{intop1=n1.n,op2=n2.n;
intd=op1-op2;if(d<0)thrownewErrDiff(op1,op2);
EntNatres=newEntNat(d);
returnres;
}
public static EntNat prod (EntNat n1, EntNat n2) throws ErrProd,
ErrConst
{intop1=n1.n,op2=n2.n;
longp=(long)op1*(long)op2;
if(p>Integer.MAX_VALUE)thrownewErrProd(op1,op2);
returnnewEntNat((int)p);
}
publicintgetN(){returnn;}
privateintn;
}
classErrNatextendsException{}
classErrConstextendsErrNat
229
{publicErrConst(intn){this.n=n;}
publicintn;
}
classErrOpextendsErrNat
{publicErrOp(intn1,intn2)
{this.n1=n1;this.n2=n2;
}
publicintn1,n2;
}
classErrSomextendsErrOp
{publicErrSom(intn1,intn2)
{super(n1,n2);
}
}
classErrDiffextendsErrOp
{publicErrDiff(intn1,intn2)
{super(n1,n2);
}
}
classErrProdextendsErrOp
{publicErrProd(intn1,intn2)
{super(n1,n2);
}
}
IlfautbienprendregardeàmentionnerErrConstdanslaclausethrowsdesméthodessomme, diff et prod puisque l’appel du constructeur de EntNat est une sourcepotentielle d’une telle exception. Si on ne le fait pas, on obtiendra une erreur decompilation.Notezque,danslaméthodeprod,ilafalluprendrelaprécautiond’effectuerlecalculduproduitenlong.Pourcela,ilnefautsurtoutpassecontenterd’écrire:longp=op1*op2;
car le produitop1*op2 serait effectué dans le type int. Le résultat ne serait jamaissupérieuràInteger.MAX_VALUE;deplus,ilpourraitêtrenégatif,cequidéclencheraituneexceptionlorsdelaconstructiondeEntNat((int)p).Voicideuxexemplesd’utilisationrépondantauxconditionsimposéesparl’énoncé:publicclassTstEntN2
{publicstaticvoidmain(Stringargs[])
{try
230
{EntNatn1=newEntNat(20),n2=newEntNat(12);
EntNatd;
d=EntNat.diff(n1,n2);
d=EntNat.diff(n2,n1);
}
catch(ErrNate)
{System.out.println("***erreurEntiernaturel****");
}
try
{EntNatn3=newEntNat(50000),n4=newEntNat(45000);
EntNatd=EntNat.diff(n3,n4);
EntNats=EntNat.somme(n3,n4);
EntNatp=EntNat.prod(n3,n4);
}
catch(ErrConste)
{ System.out.println ("*** erreur construction EntNat avec
argument"
+e.n);
}
catch(ErrDiffe)
{System.out.println("***erreurdifferenceEntNat-valeurs"
+e.n1+""+e.n2);
}
catch(ErrSome)
{System.out.println("***erreursommeEntNat-valeurs"
+e.n1+""+e.n2);
}
catch(ErrProde)
{System.out.println("***erreurproduitEntNat-valeurs"
+e.n1+""+e.n2);
}
}
}
***erreurEntiernaturel****
***erreurproduitEntNat-valeurs5000045000
1.Sinotredeuxièmebloctrynecomportaitpasl’appeldestroisméthodessomme,
231
diff etprod, le compilateur n’accepterait pas qu’il soit suivi d’unoude plusieursgestionnairesnonutiles(parexemple,catch(ErrProde)sansappeldeEntNat.prod).2.Nouspourrionsexploiterl’existencedelaclasseErrOppoursimplifierlagestiondes exceptions en nous contentant de distinguer les exceptions de construction decelles de calcul. Dans ce dernier cas, on pourrait afficher les valeurs des deuxopérandesmaisonnepourraitpluspréciserl’opérationconcernée.
232
Chapitre9
Lesbasesdelaprogrammationévénementielle
Connaissancesrequises
•LaclasseJFrame:méthodessetSize,setTitle,setBounds,setVisible
•Notiond’événement,decatégoried’événements,desourceetd’écouteur
•GestiondesévénementsdelacatégorieMouseEventavecunécouteurimplémentantl’interfaceMouseListenerouavecunécouteurdérivédelaclasseadaptateurMouseAdapter(éventuellementavecuneclasseanonyme)
•Utilisationdel’informationassociéeàunévénementdetypeMouseEvent:méthodesgetXetgetY
•Créationd’unobjetbouton(JButton),ajoutàunefenêtre(méthodesgetContentPaneetadd)
•Notiondegestionnairedemiseenforme;remplacementdugestionnairepardéfautdeJFrameparungestionnairedetypeFlowLayout
•EvénementdetypeActionEvent;méthodesactionPerformedetgetSource;notiondechaînedecommande;méthodegetActionCommand
•Suppressiond’uncomposantparlaméthoderemovedesonconteneur;méthodesvalidateetrevalidate
•Activationoudésactivationd’uncomposant:méthodesetEnabled;testd’activationparisEnabled
•Notiondepanneau(JPanel);gestionnairedemiseenformepardéfaut
233
•DessinpermanentdansunpanneauparredéfinitiondepaintComponent;notiondecontextegraphique(classeGraphics);forçagedudessinavecrepaint;tracédelignesavecdrawLine
•Dessin"àlavolée"
•QuelquesconstantesusuellesdelaclasseColor(yellow,green,red…)
•Gestiondesdimensions:obtentiondesdimensionsdel’écran(méthodegetScreenSizedelaclasseToolkit),obtentiondesdimensionsd’uncomposant(méthodegetSize),choixdesdimensionspréférentiellesd’uncomposant(méthodegetPreferredSize)
Note : En général, on s’arrange pour que la fermeture de la fenêtre graphiqueprincipalemettefinauprogrammecorrespondant.Pourobtenircerésultat,ilfauttraiterdefaçonappropriéel’événement"fermeturedelafenêtre".Ici,iln’estpasdemandédelefaire(nousyreviendronsauchapitre12) ;noussupposeronsquec’est l’utilisateurlui-mêmequimetfinauprogramme(sousUnixouLinux,ilfrapperaCtrl/Cenfenêtreconsole,sousWindows,ilfermeralafenêtreconsole).OntrouveralalistedescomposantsgraphiquesetdeleursméthodesenAnnexeB,lalistedesévénementsetdeleursméthodesenAnnexeC.
234
100Écouteursdeclicsd’unefenêtre
Écrire un programme qui crée une fenêtre (de type JFrame) et qui détecte lesévénements"appui"et"relâchement"associésàlasourisetayantlafenêtrecommesource.Onsecontenteradesignalerchacundecesdeuxévénementsenaffichantenfenêtreconsoleunmessageprécisantsanature(appuiourelâchement),ainsiquelescoordonnéescorrespondantes.Onproposeraquatresolutions:1. la fenêtre est son propre écouteur de souris et elle implémente l’interfaceMouseListener,2.onutiliseunécouteurdifférentdelafenêtre,objetd’uneclasse implémentantl’interfaceMouseListener,3.onutiliseunobjetécouteurdifférentdelafenêtreenrecourantà l’adaptateurMouseAdapter,4. on utilise un écouteur différent de la fenêtre, objet d’une classe anonymedérivéedeMouseAdapter.
OncréeuneclassefenêtrenomméeMaFenetredérivéedeJFrame.Ici,sontitreetsesdimensions seront fixés dans son constructeur à l’aide des méthodes setTitle etsetBounds.Onluiassocieraunécouteurdesévénementssourisàl’aidedelaméthodeaddMouseListener à laquelle on spécifiera l’objet écouteur voulu, à savoir ici lafenêtreelle-même(this).Ici,cetappelpeutsefairedansleconstructeur(maiscen’estpasuneobligation).Un objet écouteur doit implémenter une interface donnée (iciMouseListener). Ondevradoncmentionner la clause implementsMouseListener dans la définition de laclasseMaFenetreetdéfinirdemanièreappropriéelesméthodesquinousintéressent.Ici,ils’agitdemousePressed(appuisurunetouchequelconque)etdemouseReleased(relâchement). Notez bien que les autres méthodes de l’interface MouseListener(mouseClicked, mouseEntered et mouseExited) doivent être présentes (nous leurprévoyons simplement un corps vide).Les coordonnées de la souris sont obtenues àl’aidedesméthodesgetXetgetYqu’onappliqueàl’objetdetypeMouseEventreçuenargumentdechacunedesméthodesdel’écouteur.
235
LeprogrammesecontentedecréerunobjetdetypeMaFenetreetdel’afficherenlerendantvisibleparappeldesaméthodesetVisible.
importjavax.swing.*;//pourJFrame
importjava.awt.event.*;//pourMouseEventetMouseListener
classMaFenetreextendsJFrameimplementsMouseListener
{publicMaFenetre()//constructeur
{setTitle("Gestiondeclics");
setBounds(10,20,300,200);
addMouseListener(this);//lafenetreserasonpropreécouteur
//d'événementssouris
}
publicvoidmousePressed(MouseEventev)
{System.out.println("appuien"+ev.getX()+""+ev.getY());
}
publicvoidmouseReleased(MouseEventev)
{ System.out.println ("relachement en " + ev.getX() + " " +
ev.getY());
}
publicvoidmouseClicked(MouseEventev){}
publicvoidmouseEntered(MouseEventev){}
publicvoidmouseExited(MouseEventev){}
}
publicclassClic1
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
appuien17274
relachementen17274
appuien166126
relachementen166126
appuien7275
relachementen239131
appuien4985
relachementen-57100
236
1.Ici,laméthodesetVisibleaétéappeléedanslaméthodemain.Rienn’empêcheraitdel’appelerdansleconstructeurdeMaFenetre.2. On constate qu’un clic génère deux évènements : appui et relâchement. Endéfinissant de façon appropriée la méthode mouseClicked, on pourrait constaterqu’ilconduitégalementàunévénementclic(onditaussi"cliccomplet").3.Lesclicsopérésendehorsdelafenêtrenesontpasprisencompte.Toutefois,sil’on déplace la souris après avoir appuyé sur un bouton alors qu’elle se trouvaitdanslafenêtre,unévénementrelâchementseragénérémêmesileboutonestrelâchéendehorsde lafenêtre ;c’estcequiseproduitdans ledernierexemple,d’oùunecoordonnée négative. En revanche, si le déplacement de la souris se fait del’extérieur vers l’intérieur de la fenêtre, aucun événement ne sera signalé (la"source"concernéeparlerelâchementétantcelleconcernéeparl’appui).
Cette fois, il estnécessairededéfiniruneclassedistinctedeMaFenetre (iciEcout)implémentant l’interfaceMouseListener. La définition desméthodes concernées peutcependant rester lamêmequeprécédemment.Dans leconstructeurdeMaFenetre,onassocieàlafenêtreunobjetécouteurdetypeEcoutparadd(newEcout()).
importjavax.swing.*;//pourJFrame
importjava.awt.event.*;//pourMouseEventetMouseListener
classMaFenetreextendsJFrame
{publicMaFenetre()//constructeur
{setTitle("Gestiondeclics");
setBounds(10,20,300,200);
addMouseListener (new Ecout()) ; // on ecoute avec un objet de
typeEcout
}
}
classEcoutimplementsMouseListener
{publicvoidmousePressed(MouseEventev)
{System.out.println("appuien"+ev.getX()+""+ev.getY());
}
publicvoidmouseReleased(MouseEventev)
{ System.out.println ("relachement en " + ev.getX() + " " +
ev.getY());
}
237
publicvoidmouseClicked(MouseEventev){}
publicvoidmouseEntered(MouseEventev){}
publicvoidmouseExited(MouseEventev){}
}
publicclassClic2
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
Ici encore, on définit une classeEcout, distincte deMaFenetre. Mais cette fois, ils’agitd’uneclassedérivéedelaclasseadaptateurMouseAdapter. Ilnoussuffitalorsd’y redéfinir les deux méthodes qui nous intéressent, à savoir icimousePressed etmouseReleased ; contrairement à ce qui passait précédemment, nous n’avons plusbesoindenouspréoccuperdes autres (elles sont toutesdéfiniesdansMouseAdapteravecuncorpsvide).
importjavax.swing.*;//pourJFrame
importjava.awt.event.*;//pourMouseEventetMouseListener
classMaFenetreextendsJFrame
{publicMaFenetre()//constructeur
{setTitle("Gestiondeclics");
setBounds(10,20,300,200);
addMouseListener (new Ecout()) ; // on ecoute avec un objet de
typeEcout
}
}
classEcoutextendsMouseAdapter
{publicvoidmousePressed(MouseEventev)
{System.out.println("appuien"+ev.getX()+""+ev.getY());
}
publicvoidmouseReleased(MouseEventev)
{ System.out.println ("relachement en " + ev.getX() + " " +
ev.getY());
}
}
publicclassClic3
238
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
Cette fois,oncréeunobjetd’uneclasseanonymedérivéedeMouseAdaptereton letransmet à la méthode addMouseListener. Rappelons que les classes anonymes nepeuventêtrequedesclassesdérivéesouimplémentantuneinterface.Ici,nouscréonsnotreobjetécouteurdecettefaçon:
newMouseAdapter()
{//redéfinitiondesméthodesmousePressedetmouseReleased}
Nous y redéfinissons comme précédemment les méthodes mousePressed etmouseReleased et nous fournissons cet objet en argument de la méthodeaddMouseListener.
importjavax.swing.*;//pourJFrame
importjava.awt.event.*;//pourMouseEventetMouseListener
classMaFenetreextendsJFrame
{publicMaFenetre()//constructeur
{setTitle("Gestiondeclics");
setBounds(10,20,300,200);
addMouseListener(newMouseAdapter()
{publicvoidmousePressed(MouseEventev)
{ System.out.println ("appui en " + ev.getX() + " " +
ev.getY());
}
publicvoidmouseReleased(MouseEventev)
{ System.out.println ("relachement en " + ev.getX() + " " +
ev.getY());
}
});
}
}
publicclassClic4
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
239
fen.setVisible(true);
}
}
240
101Écouteursdeclicsdeplusieursfenêtres
Écrireunprogrammequicréeplusieursfenêtres(detypeJFrame)repéréesparunnuméroetquidétectelesévénements"appui"et"relâchement"delasourisassociésà chacune de ces fenêtres.On signalera chaque événement en affichant en fenêtreconsoleunmessageprécisantsanature(appuiourelâchement),lenumérodefenêtreetlescoordonnéescorrespondantes.Onproposeradeuxsolutions:1.chaquefenêtreestsonpropreécouteurdesouris,2. chaque fenêtre dispose d’un objet écouteur d’une classe implémentantl’interfaceMouseAdapter.
Notezqu’ils’agitdelagénéralisationdel’exerciceàplusieursfenêtres.
On va donc être amené à créer une classe spécialisée (iciMaFenetre) dérivée deJFrame.Ilestpréférablequelenombredefenêtresàcréernesoitpasimposéparlaclasse. Ici, il est fixépar une constante (nFen=3) définiedans laméthodemain. Enrevanche, le constructeur deMaFenetre devra être enmesure d’attribuer un numérodifférent à chaque fenêtre, numéro qui se retrouvera dans son titre et surtout dans lemessage correspondant aux événements signalés. Pour ce faire, nous comptonssimplement les objets du type MaFenetre en employant une variable de classe(statique)nbFen,initialiséeàzéroetincrémentéeàchaquecréationd’unnouvelobjet.Unchampnumsertàconserverlenuméroattribuéàunefenêtredonnée.Comme chaque fenêtre est son propre écouteur, les méthodes mousePressed etmouseReleased accèdent directement au champ num pour en afficher la valeur, enmêmetempsquelescoordonnéesduclic.
importjavax.swing.*;//pourJFrame
importjava.awt.event.*;//pourMouseEventetMouseListener
classMaFenetreextendsJFrameimplementsMouseListener
{publicMaFenetre()
{nbFen++;
241
num=nbFen;
setTitle("Fenetrenumero"+num);
setBounds(10,20,300,200);
addMouseListener(this);
}
publicvoidmousePressed(MouseEventev)
{System.out.println("appuidansfennum"+num
+"en"+ev.getX()+""+ev.getY());
}
publicvoidmouseReleased(MouseEventev)
{System.out.println("relachementdansfennum"+num
+"en"+ev.getX()+""+ev.getY());
}
publicvoidmouseClicked(MouseEventev){}
publicvoidmouseEntered(MouseEventev){}
publicvoidmouseExited(MouseEventev){}
privatestaticintnbFen=0;
privateintnum;
}
publicclassClicI1
{publicstaticvoidmain(Stringargs[])
{finalintnFen=3;
for(inti=0;i<nFen;i++)
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
}
appuidansfennum1en12182
relachementdansfennum1en12182
appuidansfennum1en16891
relachementdansfennum1en456155
appuidansfennum2en228137
relachementdansfennum2en228137
appuidansfennum3en152169
relachementdansfennum3en152169
appuidansfennum3en112121
relachementdansfennum3en-2341
appuidansfennum2en89119
242
relachementdansfennum2en89119
Pournuméroter les fenêtres,nousemployons lamêmedémarchequedans la solutionprécédente(champstatiquenbFenetchampnumdanslaclasseMaFenetre).En revanche, cette fois, les objets écouteurs sont d’une classedistinctede la fenêtre(nomméeiciEcout).Ilfautdoncquechaqueobjetécouteurdisposed’uneinformationlui permettant d’identifier la fenêtre à laquelle il est associé. Une façon de faireconsisteàfournircenuméroauconstructeurdel’objetécouteuretàleconserverdansunchamp(icinum).
importjavax.swing.*;
importjava.awt.event.*;
classMaFenetreextendsJFrame
{publicMaFenetre()
{nbFen++;
num=nbFen;
setTitle("Fenetrenumero"+num);
setBounds(10,20,300,200);
addMouseListener (new Ecout(num)) ; // chaque fenetre a son
propreecouteur
}
privatestaticintnbFen=0;
privateintnum;
}
classEcoutextendsMouseAdapter
{publicEcout(intnum)
{this.num=num;
}
publicvoidmousePressed(MouseEventev)
{System.out.println("appuidansfennum"+num
+"en"+ev.getX()+""+ev.getY());
}
publicvoidmouseReleased(MouseEventev)
{System.out.println("relachementdansfennum"+num
+"en"+ev.getX()+""+ev.getY());
}
private int num ; // numero de la fenetre ecoutee public void
mousePressed
243
(MouseEventev)
}
publicclassClicI2
{publicstaticvoidmain(Stringargs[])
{finalintnFen=3;
for(inti=0;i<nFen;i++)
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
}
244
102Écouteurcommunàplusieursfenêtres
Écrireunprogrammequicréeplusieursfenêtres(detypeJFrame)etquidétectelesévénements "appui" et "relâchement" de la souris associés à chacune de cesfenêtres.Onsignalerachaqueévénementenaffichantenfenêtreconsoleunmessageprécisant sa nature (appui ou relâchement) et les coordonnées correspondantes(notezbienqu’ici,onnechercheplusàafficherunnumérodefenêtre).Onproposeraunesolutionavecunseulobjetécouteurpourtouteslesfenêtres.
Pournuméroterlesfenêtres,nousutiliseronslamêmedémarchequedansl’exercice94.Cettefois,contrairementàcequisepassaitdansladeuxièmesolutiondel’exercice94,onsouhaitedisposerd’unseulobjetécouteurcommunàtouteslesfenêtres.Ildoitdoncêtre créé avant toute fenêtre. Pour y parvenir, nous pouvons utiliser dans la classeMaFenetreunblocd’initialisationstatique(bloc introduitpar lemotcléstatic)dontonsaitqu’ilestexécutéavanttoutecréationd’objet.Bienentendu,nousfaisonsalorsdelaréférenceàl’écouteur(ec)unchampdeclasse(static).
importjavax.swing.*;
importjava.awt.event.*;
classMaFenetreextendsJFrame
{
publicMaFenetre()//constructeur
{nbFen++;
num=nbFen;
setTitle("Fenetrenumero"+num);
setBounds(10,20,300,200);
addMouseListener(ec);
}
privateintnum;
privatestaticEcoutec;
static//blocstatiqueexecuteavantl'instanciationd'unobjet
dutype
245
{ec=newEcout();
}
privatestaticintnbFen=0;
}
classEcoutextendsMouseAdapter
{publicvoidmousePressed(MouseEventev)
{System.out.println("appuien"+ev.getX()+""+ev.getY());
}
publicvoidmouseReleased(MouseEventev)
{ System.out.println ("relachement en " + ev.getX() + " " +
ev.getY());
}
privateintnum;//numerodelafenetreecoutee
publicclassClicI3
{publicstaticvoidmain(Stringargs[])
{finalintnFen=3;
for(inti=0;i<nFen;i++)
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
}
Ici,onnedemandaitpasd’afficher lenumérode fenêtre.Pouryparvenir, il faudraittenircomptedecequel’objetécouteurestcommunà toutes lesfenêtres.Onpourraitpar exemple identifier la source de l’événement avec la méthode getSource de laclassemouseEvent,cequinousfourniraitlaréférencedelafenêtrecorrespondante.Ilfaudraitensuitedisposerd’unmoyenpermettantdeluifairecorrespondrelenumérodefenêtre,cequinécessiteraitd’accéderàunelistedesréférencesdefenêtres.
246
103Créationdeboutonsetchoixd’ungestionnaireFlowLayout
Écrire un programme qui crée une fenêtre (JFrame) et qui y affiche n boutonsportantlesétiquettesBOUTON1,BOUTON2…disposéscommedanscetexemple:
Lavaleurdenseralueauclaviera.
a.OnpourrautiliserlaméthodelireIntdelaclasseClavierfourniesurlesiteWebd’accompagnementetdontlalistefigureenAnnexeD.
Pourcréerlesboutons,noustransmettonsàleurconstructeurl’étiquettequ’onsouhaitey voir figurer ; ici, il s’agira de la concaténation de la chaîne "BOUTON" avec lenumérodubouton.Rappelonsqu’onajouteuncomposant(telunbouton),nonpasdirectementàunobjetfenêtre(typeJFrameoudérivé),maisàsoncontenu(objetdetypeContainer)dontonobtientlaréférenceàl’aidedelaméthodegetContentPanedelaclasseJFrame.Par ailleurs, ici, on ne peut pas se contenter d’utiliser le gestionnaire par défaut deJFrame qui est de type BorderLayout (il ne permet de placer qu’au maximum 5composants). Il faut utiliser un gestionnaire de type FlowLayout. Le choix d’ungestionnaire se fait à l’aide de la méthode setLayout qu’on applique là encore aucontenudelafenêtre,etàlaquelleontransmetenargumentlaréférenced’unobjetdutypevoulu.
importjavax.swing.*;
247
importjava.awt.*;
classFenBoutonsextendsJFrame
{
publicFenBoutons(intnBout)
{setTitle("BOUTONS");
setSize(200,150);
Containercontenu=getContentPane();
contenu.setLayout(newFlowLayout());
for(inti=0;i<nBout;i++)
{unBouton=newJButton("BOUTON"+(i+1));
contenu.add(unBouton);
}
}
privateJButtonunBouton;
}
publicclassBoutons
{publicstaticvoidmain(Stringargs[])
{System.out.print("Combiendeboutons?");
intnBoutons=Clavier.lireInt();
FenBoutonsfen=newFenBoutons(nBoutons);
fen.setVisible(true);
}
}
Ici, nous n’avons pas conservé la référence de chacun des boutons créés ; celle-cin’aurafiguréquetemporairementdanslavariableunBouton.
248
104Gestiondeplusieursboutonsd’unefenêtreavecunseulécouteur
Adapter le programme de l’exercice 96 pour qu’il détecte les actions sur lesdifférentsboutons.Onproposeradeuxsolutions:1. la fenêtre est l’écouteur de tous les boutons et on recourt à getSource pouridentifierleboutonconcerné;chaqueactionsurunboutonaffichesonnuméroenfenêtreconsolecommedanscetexemple:Combiendeboutons?5
Actionsurbouton1
Actionsurbouton4
Actionsurbouton5
Actionsurbouton5
Actionsurbouton3
2.onutiliseunobjetécouteur (unique)différentde la fenêtreeton recourtà laméthodegetActionCommand pour identifier le bouton concerné ; chaque actionsur un bouton affiche en fenêtre console une ligne formée d’un nombred’astérisqueségalaunumérodubouton,commedanscetexempled’exécution:Combiendeboutons?5
*****
*
****
**
***
DanslaclasseFenBoutons,nousdevonsdoncimplémenterl’interfaceActionListenerendéfinissantlaméthodeactionPerformed.Commel’énoncénousimposederecourirà laméthodegetSource de la classeActionEvent (elle fournit la référence à l’objetsource de l’événement), il nous faut conserver les références des différents boutonsdansl’objetfenêtre.Pourcefaire,nousyintroduisonsuntableauderéférencesàdesboutons(boutons).
249
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classFenBoutonsextendsJFrameimplementsActionListener
{publicFenBoutons(intnBout)
{this.nBout=nBout;
setTitle("BOUTONS");
setSize(200,150);
Containercontenu=getContentPane();
contenu.setLayout(newFlowLayout());
boutons=newJButton[nBout];
for(inti=0;i<nBout;i++)
{boutons[i]=newJButton("BOUTON"+(i+1));
contenu.add(boutons[i]);
boutons[i].addActionListener(this);
}
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
for(inti=0;i<nBout;i++)
if(source==boutons[i])
System.out.println("Actionsurbouton"+(i+1));
}
privateintnBout;
privateJButton[]boutons;
}
publicclassBoutons1
{publicstaticvoidmain(Stringargs[])
{System.out.print("Combiendeboutons?");
intnBoutons=Clavier.lireInt();
FenBoutonsfen=newFenBoutons(nBoutons);
fen.setVisible(true);
}
}
1.DanslaméthodeactionPerformed,labouclederecherchedelasourcen’estpas
250
optimiséepuisqu’ellesepoursuitlorsqu’onaidentifiélasource.2. Notez la comparaison (légale) source == boutons[i] qui fait intervenir unopérandedetypeObjectetunopérandedetypeJButton.LesecondestsimplementconvertienObject,cequinemodifiepaslavaleurdelaréférencecorrespondante.CommeicinoussommescertainsquelasourceestdetypeJButton,nousaurionspuégalementprocéderainsi:
JButtonsource=(JButton)e.getSource();
for(inti=0;i<nBout;i++)
if(source==boutons[i])…
Ici, nous faisons en sorte que le programme permette facilement la modification dupréfixe (iciBOUTON) de l’étiquette associée aux boutons. Celui-ci est défini en unseulendroitduconstructeurdelafenêtre(prefixeBouton).Cettefois,l’écouteurestunobjetd’uneclassedistinctedecelledelafenêtre,nomméeEcout.ElledéfinitlaméthodeactionListenerenyretrouvantla"chaînedecommande"associée à l’action. Rappelons que, par défaut, celle-ci n’est rien d’autre quel’étiquettedubouton.Enextrayantlafindecettechaîne,nousobtenonsunesous-chaînecorrespondantaunuméroetnouslaconvertissonsenunentierparInteger.parseInt.Notezqu’icinousavonsdûprévoirunconstructeurpourlaclasseEcoutdans leseulbutd’yrécupérerlepréfixedesétiquettesdesboutons.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classFenBoutonsextendsJFrame
{publicFenBoutons(intnBout)
{finalStringprefixeBouton="BOUTON";
this.nBout=nBout;
setTitle("BOUTONS");
setSize(200,150);
Containercontenu=getContentPane();
contenu.setLayout(newFlowLayout());
boutons=newJButton[nBout];
Ecoutecouteur=newEcout(prefixeBouton);
for(inti=0;i<nBout;i++)
{boutons[i]=newJButton(prefixeBouton+(i+1));
contenu.add(boutons[i]);
251
boutons[i].addActionListener(ecouteur);
}
}
privateintnBout;
privateJButton[]boutons;
}
classEcoutimplementsActionListener
{publicEcout(Stringprefixe)
{this.prefixe=prefixe;
}
publicvoidactionPerformed(ActionEvente)
{Stringcommande=e.getActionCommand();
StringchNum=commande.substring(prefixe.length());
intnum=Integer.parseInt(chNum);
for(inti=0;i<num;i++)
System.out.print("*");
System.out.println();
}
privateStringprefixe;
}
publicclassBoutons2
{publicstaticvoidmain(Stringargs[])
{System.out.print("Combiendeboutons?");
intnBoutons=Clavier.lireInt();
FenBoutonsfen=newFenBoutons(nBoutons);
fen.setVisible(true);
}
}
Au lieu de chercher à extraire un numéro de bouton de son étiquette, nous aurionségalementpumodifier la chaînedecommandedechacundesboutons, enutilisant laméthodesetActionCommand:
boutons[i].setActionCommand(String.valueOf(i+1));
LaclasseEcoutpourraitseprésenterainsi(ellen’auraitplusbesoindeconstructeur):classEcoutimplementsActionListener
{publicvoidactionPerformed(ActionEvente)
{Stringcommande=e.getActionCommand();
252
intnum=Integer.parseInt(commande);
for(inti=0;i<num;i++)
System.out.print("*");
System.out.println();
}
}
LeprogrammecompletainsimodifiéfiguresurlesiteWebd’accompagnementsouslenomBoutonsb.java.
253
105Synthèse:créationetsuppressiondeboutons(1)
Écrireunprogrammequi afficheune fenêtrecomportantdeuxboutonsd’étiquettes"CREATION"et"SUPPRESSION"placésrespectivementenhautetenbas.ChaqueactionsurleboutonCREATIONconduiraàlacréationd’unboutonjauneàl’intérieur de la fenêtre. Chaque action sur l’un des boutons de la fenêtre le"sélectionnera"(s’ilnel’estpasdéjà)oule"désélectionnera"(s’il l’estdéjà).Onvisualisera un bouton sélectionné en le colorant en rouge. Chaque action sur leboutonSUPPRESSIONsupprimeratouslesboutonssélectionnés(rouges).Lesboutonsserontnumérotésdansl’ordredeleurcréation.Onneréutiliserapaslesnumérosdesboutonssupprimés.
Par souci de simplicité, on fournira au constructeur de la fenêtre le nombremaximumdeboutonssusceptiblesd’êtrecréés.
Comme le suggère l’image fournie dans l’énoncé, les deux boutons CREATION etSUPPRESSIONpeuventêtredisposésdanslafenêtreenutilisantsongestionnairepardéfautdetypeBorderLayout.Ilsuffirasimplementdepréciserlesparamètres"North"et"South".Enrevanche,lesboutonsgérésdynamiquementdevrontêtreplacésdansunpanneaudistinctqu’onplaceraaucentredelafenêtre(optionpardéfautdelaméthodeadd). Le gestionnaire par défaut d’un panneau est de typeFlowLayout, ce qui nousconviendraici.
254
Nousfaisonsdelafenêtrel’écouteurdetouslesboutons.Commeilestnécessairedeconserver une information de couleur pour chacun des boutons dynamiques, nousprévoyons à cet effet un tableau boutons comportant les références des boutonsdynamiquesetuntableauboutSeleccontenantuneinformationbooléennedesélection.Cesdeuxtableauxaurontunetaillefournielorsdel’appelduconstructeurdelafenêtre.Chaquefoisqu’onmodifielecontenudupanneau,soitenajoutantunnouveaubouton,soitensupprimantlesboutonssélectionnés,onfaitappelàsaméthodevalidate,afindeforcer son gestionnaire de mise en forme à recalculer les positions des différentscomposants.Enrevanche,cetappeln’estpasnécessairelorsdelamodificationdelacouleurd’unbouton(parsetBackground)carilestalorsréaliséautomatiquement.NousutilisonsclassiquementunevariablestatiquenBoutpournuméroternosboutons.Onnoteraquelepremierboutonportelenuméro1,alorsqu’ilcorrespondàl’indice0danslestableauxboutonsetboutSelec.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classFenBoutDynextendsJFrameimplementsActionListener
{publicFenBoutDyn(intnBoutMax)
{setTitle("Creation-suppressiondeboutons(maxi"+nBoutMax+
")");
setSize(500,180);
Containercontenu=getContentPane();
creation=newJButton("CREATION");
contenu.add(creation,"North");
creation.addActionListener(this);
suppression=newJButton("SUPPRESSION");
contenu.add(suppression,"South");
suppression.addActionListener(this);
pan=newJPanel();
contenu.add(pan);//aucentrepardefaut
boutons=newJButton[nBoutMax];
boutSelec=newboolean[nBoutMax];
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
255
if(source==creation)
{boutons[nBout]=newJButton("BOUTON"+(nBout+1));
boutons[nBout].setBackground(Color.yellow);
boutSelec[nBout]=false;
pan.add(boutons[nBout]);
boutons[nBout].addActionListener(this);
pan.validate();//pourforcerlerecalculparlegestionnaire
nBout++;
}
if(source==suppression)
{for(inti=0;i<nBout;i++)
if(boutSelec[i])pan.remove(boutons[i]);;
pan.validate();
}
for(inti=0;i<nBout;i++)
{if(source==boutons[i])
if(boutSelec[i])
{boutSelec[i]=false;
boutons[i].setBackground(Color.yellow);
}
else
{boutSelec[i]=true;
boutons[i].setBackground(Color.red);
}
}
}
privateJButtoncreation,suppression;
privateJPanelpan;
privatestaticintnBout=0;
privateJButton[]boutons;
privateboolean[]boutSelec;
}
publicclassCrSuprB
{publicstaticvoidmain(Stringargs[])
{FenBoutDynfen=newFenBoutDyn(50);
fen.setVisible(true);
}
256
}
257
106Synthèse:créationetsuppressiondeboutons(2)
Écrire un programme qui affiche une fenêtre comportant deux boutons placésrespectivementenhautetenbas.Chaqueactionsurl’undecesboutonsconduiraàlacréationd’unboutonàl’intérieurdelafenêtre.Leboutonduhautcréerades"grosboutons"tandisqueceluidubascréeradesboutonspluspetits.Lesgrosboutonsafficheront lenombrede foisoù l’ona agit sur eux.Lorsquecenombreatteindra5,ilsserontsupprimésdelafenêtre.Lespetitsboutonsserontsupprimésdèslapremièrefoisoùl’onagitsureux.
Lesécouteursdesgrosetdespetitsboutonsdevrontêtredistinctsdelafenêtre.
Comme le suggère l’image fournie dans l’énoncé, les deux boutons marqués GROSBOUTONet PETITBOUTONpeuvent être disposés dans la fenêtre en utilisant songestionnaire par défaut de typeBorderLayout. Il suffira simplement de préciser lesparamètres"North"et"South".Enrevanche,lesboutonsgérésdynamiquementdevrontêtreplacésdansunpanneaudistinctqu’onplaceraaucentredelafenêtre(optionpardéfaut de la méthode add). Le gestionnaire par défaut d’un panneau est de typeFlowLayout,cequinousconviendraici.Comme l’énoncé ne nous impose pas de contraintes particulières, nous ferons de lafenêtrel’écouteurdecesdeuxboutons.
258
Encequiconcernelesboutonsgérésdynamiquement,l’énoncénousimposed’utiliserunécouteurdistinctdelafenêtre.Lasolutionlaplussimpleconsistealorsàcréerdeuxclasses d’écouteurs différentes : EcouteGrosBouton et EcoutePetitBouton. Il estnécessaired’associeràchaquegrosboutonuneinformationcorrespondantaunombred’actions ; dans ces conditions, il est préférable d’utiliser un objet écouteur (de laclasse EcouteGrosBouton) pour chacun. En revanche, aucune information n’est àmémoriserpour lespetits boutons, de sortequ’onpourra se contenterde les écoutertousaveclemêmeobjetécouteur(detypeEcoutePetitBouton).Notez qu’ici, contrairement à ce qui se produisait dans l’exercice 105, il n’est pasnécessairedeconserver les référencesdesboutonsdupanneau.Par contre, il faudraque les écouteursdesboutonsdynamiquesdisposentde la référencedupanneau ;onpourralafourniràleurconstructeur.La taille des gros boutons et celle des petits boutons sera imposée à l’aide de laméthodesetPreferredSizedelaclasseJButton.EllenécessiteenargumentunobjetdetypeDimensiondontonfournitlesvaleursenargumentdesonconstructeur.Rappelonsque cette information est exploitée correctement par un gestionnaire de typeFlowLayout,maisqu’iln’envapasdemêmepour tous lesgestionnairesdemiseenforme.
importjavax.swing.*;importjava.awt.*;importjava.awt.event.*;
classFenBoutDynextendsJFrameimplementsActionListener
{publicstaticDimensiondimPetitBouton=newDimension(70,30),
dimGrosBouton=newDimension(110,50);
publicstaticStringetiqCompt="COMPTE=";
publicFenBoutDyn()
{setTitle("GrosetPetitsBoutons");
setSize(500,200);
Containercontenu=getContentPane();
grosBouton=newJButton("GROSBOUTON");
contenu.add(grosBouton,"North");
grosBouton.addActionListener(this);
petitBouton=newJButton("PETITBOUTON");
contenu.add(petitBouton,"South");
petitBouton.addActionListener(this);
pan=newJPanel();
contenu.add(pan);//aucentrepardefaut
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
259
if(source==grosBouton)
{JButtonbouton=newJButton(etiqCompt);
pan.add(bouton);
bouton.addActionListener(newEcoutGrosBouton(pan,etiqCompt));
bouton.setPreferredSize(dimGrosBouton);
pan.validate();
}
if(source==petitBouton)
{JButtonbouton=newJButton("Petit");
pan.add(bouton);
bouton.addActionListener(newEcoutePetitBouton(pan));
bouton.setPreferredSize(dimPetitBouton);
pan.validate();
}
}
privateJButtonpetitBouton,grosBouton;
privateJPanelpan;
}
classEcoutGrosBoutonimplementsActionListener
{staticintnMaxClics=5;
publicEcoutGrosBouton(JPanelpan,StringetiqCompt)
{nActions=0;
this.pan=pan;
this.etiqCompt=etiqCompt;
}
publicvoidactionPerformed(ActionEvente)
{JButtonbouton=(JButton)e.getSource();
nActions++;
if(nActions>=nMaxClics)
{pan.remove(bouton);
pan.validate();
}
else
{bouton.setText(etiqCompt+nActions);
}
}
privateintnActions;
privateJPanelpan;
260
privateStringetiqCompt;
}
classEcoutePetitBoutonimplementsActionListener
{publicEcoutePetitBouton(JPanelpan)
{this.pan=pan;
}
publicvoidactionPerformed(ActionEvente)
{JButtonbouton=(JButton)e.getSource();
pan.remove(bouton);
pan.validate();
}
privateJPanelpan;
}
publicclassGrosPetB
{publicstaticvoidmain(Stringargs[])
{FenBoutDynfen=newFenBoutDyn();
fen.setVisible(true);
}
}
Dans certaines des méthodes actionPerformed, nous avons utilisé des conversionsexplicitesdee.getSource()enJButton car nous étions certainsdu typede la source.Dansunprogrammepluscomplexe,ilfaudraitparfoisêtreplusprudent.Parexemple,onpourraits’assurerquelasourceestbiendetypeJButtonenutilisant:if(sourceinstanceofJButton).....
261
107Dessinpermanentdansunefenêtre
Écrire un programme qui affiche en permanence dans une fenêtre un rectangle detailledonnéeetsesdeuxdiagonales,commedanscetexemple:
Rappelonsquelorsqu’onutiliselescomposantsSwingdeJava2,ladémarchelaplusappropriée pour obtenir des dessins permanents dans une fenêtre (de type JFrame)consiste à dessiner, non pas directement dans la fenêtre elle-même, mais dans unpanneau(objetdetypeJPanel)placédanscettefenêtre.Nouscréonsdonciciunobjetd’une classe Panneau, dérivée de JPanel et nous l’ajoutons à la fenêtre par add.Comme le gestionnaire par défaut de notre fenêtre est de type BorderLayout, nousn’avonspasànouspréoccuperdelatailledupanneau1.Il suffit alors de redéfinir laméthode paintComponent du panneau en y plaçant lesinstructions de dessin voulues.Encore faut-il prendre soin d’appeler au préalable laméthode paintComponent de la classe de base JPanel, laquelle dessine le fond dupanneau(cequieffacedoncl’ancien).Nous employons la méthode drawLine pour tracer les 6 segments de droite quiconstituentlafigurevoulue2.
importjavax.swing.*;importjava.awt.*;
classMaFenetreextendsJFrame
{publicMaFenetre()
{setTitle("Dessinpermanent");
262
setSize(300,150);
pan=newPanneau();getContentPane().add(pan);
}
privatePanneaupan;
}
classPanneauextendsJPanel
{privatestaticfinalintx=30,y=20,l=120,h=50;
publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);//
//tracedurectangle
g.drawLine(x,y,x+l,y);
g.drawLine(x+l,y,x+l,y+h);
g.drawLine(x+l,y+h,x,y+h);
g.drawLine(x,y+h,x,y);
//tracedesdiagonales
g.drawLine(x,y,x+l,y+h);
g.drawLine(x,y+h,x+l,y);
}
}
publicclassDesPer
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
Vous pourrez constater que le dessin (de taille fixe) subsiste, quelles que soient lesopérationsquel’onfaitsubiràlafenêtre.
Il peut être intéressantdevoir ceque fait ceprogramme lorsqu’on supprime l’appelsuper.paintComponentdanslaméthodepaintComponent.
263
108Synthèse:dessinpermanentetchangementdecouleur
Adapterleprogrammedel’exercice100,defaçonquechaqueclic(complet)danslafenêtreenmodifielacouleur.Onsefixeraunelistedequelquescouleursqu’onparcourrademanièrecyclique.
Commelepanneaucouvretoutelafenêtre,unclicdanslafenêtreaenfaitlepanneaucomme source. Il faut donc écouter les événements de typeMouseEvent ayant poursourcelepanneau.Ici,nousfaisonsdupanneausonpropreécouteur,cequifaciliteladéfinitiondelaméthodemouseClicked.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrame
{publicMaFenetre()
{setTitle("Dessinpermanent");
setSize(300,150);
pan=newPanneau();
pan.addMouseListener (pan) ; // le panneau sera son propre
ecouteur
getContentPane().add(pan);
}
privatePanneaupan;
}
classPanneauextendsJPanelimplementsMouseListener
{privatestaticfinalintx=30,y=20,l=120,h=50;
privatestaticfinalColor[]couleurs={Color.yellow,Color.blue,
Color.green,Color.red};
publicvoidpaintComponent(Graphicsg)
264
{super.paintComponent(g);
//tracedurectangle
g.drawLine(x,y,x+l,y);
g.drawLine(x+l,y,x+l,y+h);
g.drawLine(x+l,y+h,x,y+h);
g.drawLine(x,y+h,x,y);
//tracedesdiagonales
g.drawLine(x,y,x+l,y+h);
g.drawLine(x,y+h,x+l,y);
}
publicvoidmouseClicked(MouseEvente)
{setBackground(couleurs[numCoul]);
numCoul++;
if(numCoul>=couleurs.length)numCoul=0;
}
publicvoidmousePressed(MouseEvente){}
publicvoidmouseReleased(MouseEvente){}
publicvoidmouseEntered(MouseEvente){}
publicvoidmouseExited(MouseEvente){}
privateintnumCoul=0;
}
publicclassDesCoul
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
1.Initialementlepanneauestpeintengris,etnonenjaune.Pourqu’ilsoitjaunedèsle début, il faudrait fixer la couleur de fond à couleur[0] par exemple lors de laconstructiondupanneauetfixernumCoulà1etnonà0.2. Ici, iln’estpasnécessaired’appeler laméthoderepaint aprèsavoirmodifié lacouleur de fond du panneau car cela est fait automatiquement par la méthodesetBackground. Le programme modifié dans ce sens figure sur le site Webd’accompagnementsouslenomDesCoul1.java.
265
109Synthèse:dessinpermanent,colorationetadaptationàlatailled’unefenêtre
Écrireunprogrammequiafficheenpermanencedansunefenêtreunrectangleetsesdeuxdiagonales ; lesdimensionsdu rectangleserontdéterminéesdemanièreàcequ’ilsoit toujourssituéà5pixelsdelaborduredelafenêtre.Unboutonplacéenhautdelafenêtrepermettrad’enmodifierlacouleurdefond;unautreboutonplacéenbaspermettrademodifierlacouleurdestraitsdudessin.
On se fixera une (seule) liste de quelques couleurs qu’on parcourra de manièrecyclique.
Nous conservons le gestionnaire par défaut de la fenêtre. Le dessin est fait dans unpanneau placé au centre et les deux boutons sont placés respectivement avec lesparamètres"North"et"South".Nousécoutonslesdeuxboutonsdanslafenêtreelle-mêmeetnousdotonslepanneaudedeuxméthodespubliqueschangeCoulFondetchangeCoulTrait chargées demodifierlescouleurs.DanschangeCoulTrait,ilnesuffitpasdemodifierlacouleurd’avant-plandupanneau(parsetForeground).Ilfautenoutreforcerledessinparappelderepaint.Pour adapter la tailledudessin à la fenêtre (ouplutôt aupanneau),nousutilisons laméthodegetSizequinousfournitlesdimensionsdupanneausousformed’unobjetde
266
typeDimension.
importjavax.swing.*;
importjava.awt.event.*;
importjava.awt.*;
classMaFenetreextendsJFrameimplementsActionListener
{
publicMaFenetre()
{setTitle("DessinetCouleurs");
setSize(300,150);
Containercontenu=getContentPane();
pan=newPanneau();
contenu.add(pan);
coulFond=newJButton("Couleurfond");
contenu.add(coulFond,"North");
coulFond.addActionListener(this);
coulTrait=newJButton("Couleurtrait");
contenu.add(coulTrait,"South");
coulTrait.addActionListener(this);
}
publicvoidactionPerformed(ActionEvente)
{if(e.getSource()==coulFond)pan.changeCoulFond();
if(e.getSource()==coulTrait)pan.changeCoulTrait();
}
privateintnumCouleur;
privateJButtoncoulFond,coulTrait;
privatePanneaupan;
}
classPanneauextendsJPanel
{ final Color[] couleurs = { Color.red, Color.yellow, Color.blue,
Color.green,
Color.gray,Color.pink,Color.cyan,Color.white};
publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
setBackground(couleurs[numCoulFond]);
setForeground(couleurs[numCoulTrait]);
Dimensiondim=getSize();
intx=5,y=5;
267
intl=dim.width,h=dim.height;
//tracedurectangle
g.drawLine(x,y,l-x,y);
g.drawLine(l-x,y,l-x,h-y);
g.drawLine(l-x,h-y,x,h-y);
g.drawLine(x,h-y,x,y);
//tracedesdiagonales
g.drawLine(x,y,l-x,h-y);
g.drawLine(x,h-y,l-x,y);
}
publicvoidchangeCoulFond()
{numCoulFond++;
if(numCoulFond>=couleurs.length)numCoulFond=0;
repaint();
}
publicvoidchangeCoulTrait()
{numCoulTrait++;
if(numCoulTrait>=couleurs.length)numCoulTrait=0;
repaint();
}
privateintnumCoulFond=0,numCoulTrait=1;
}
publicclassDesCoul2
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
Nous aurions pu ne pas appeler setBackground et setForeground danspaintComponentetnouscontenter:
–d’appelersetBackgrounddanschangeCoulFond,–d’appelersetForegroundetrepaintdanschangeCoulTrait.
268
110Dessinàlavolée
Écireunprogrammequidessine"auvol"dansunefenêtreenjoignantpardestraitslesdifférentspointsauquell’utilisateurclique:
Ici,onnechercherapasàassurerlapermanencedudessinquiseraeffacédèsquel’utilisateur déplace la fenêtre oumodifie sa taille (on demande toutefois que ceteffacementsoittoujourscomplet).
Bien qu’il s’agisse de dessin au vol, nous travaillons sur un panneau. Cela nouspermettrad’effacer la fenêtreendéfinissantuneméthodepaintComponent réduite ausimpleappelsuper.paintComponent.Ledessinproprementdit est réalisédans laméthodemouseClicked de l’écouteurdupanneau (ici, le panneau lui-même). Rappelons que pour ce faire, il est nécessaired’obteniruncontextegraphiquepour lepanneau(getGraphics)etde le libéreraprèsemploi(dispose).Pour traiter le premier clic différemmentdes suivants, nous employonsun indicateurbooléen(enCours)qu’onplaceàlavaleurfalseaudébutetàchaqueeffacementdelafenêtre.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrame
{publicMaFenetre()
269
{setTitle("Crayonmagique");
setSize(300,150);
pan=newPanneau();
pan.addMouseListener(pan);
getContentPane().add(pan);
}
privatePanneaupan;
}
classPanneauextendsJPanelimplementsMouseListener
{publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
enCours=false;
}
publicvoidmouseClicked(MouseEvente)
{intxFin=e.getX();yFin=e.getY();
if(enCours){Graphicsg=getGraphics();
g.drawLine(xDeb,yDeb,xFin,yFin);
g.dispose();
}
xDeb=xFin;yDeb=yFin;
enCours=true;
}
publicvoidmousePressed(MouseEvente){}
publicvoidmouseReleased(MouseEvente){}
publicvoidmouseEntered(MouseEvente){}
publicvoidmouseExited(MouseEvente){}
privatebooleanenCours=false;
privateintxDeb,yDeb,xFin,yFin;
}
publicclassDesVol
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
270
Si l’énoncé n’avait pas imposé l’effacement de la fenêtre, nous aurions pu dessinerdirectement dans la fenêtre en définissant la même méthodemouseClicked dans unécouteurquiauraitpuêtre la fenêtreelle-même.Ledessinaurait alorspuse trouvereffacé partiellement lors d’actions sur la fenêtre ; de plus la gestion de l’indicateurenCoursn’auraitplusétépossible…
271
111Synthèse:ardoisemagiqueencouleur
Adapterleprogrammedel’exercice,demanièreque:
•l’utilisateurpuissedessinerplusieurslignesbrisées(boutonNouvelleligne),
•qu’ilpuisseeffacerlecontenudelafenêtre(boutonEffacer),
•qu’ilpuissechoisiràchaqueinstantunecouleurdedessinàl’aided’unboutonplacé à gauche ; on se fixera une liste de quelques couleurs (constantes de laclasseColor) qu’on parcourra de façon cyclique ; le bouton de sélection serapeintdanslacouleurcourante:
Note : pour choisir la couleur de dessin d’un contexte graphique, on utilisera laméthodesetColordelaclasseGraphics.
Nous utilisons toujours un panneau pour dessiner. Les boutons sont placésclassiquementdans la fenêtreenutilisant lesparamètres"North","South"et"West".Leboutondesélectiondecouleurdisposed’un titre"vide"3 et sacouleurest fixéeàl’aidedesaméthodesetBackground.LagestiondudessinsefaitlàencoreavecunindicateurbooléenenCoursmais,cettefois, celui-ci doit être également réinitialisé à false lors de l’action sur le boutonNouvelleLigne.Nousavonschoisid’écouterlestroisboutonsdanslafenêtreelle-même,cequiimpose
272
un échange d’informations entre fenêtre et panneau. Pour ce faire, nous dotons notrepanneaudeméthodespubliquessetCoul,nouvelleLigneetefface.Nous faisons tout naturellement du panneau son propre écouteur de clics et nousdessinons"àlavolée"danslaméthodemouseClicked.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrameimplementsActionListener
{ public static Color[] couleurs = {Color.yellow, Color.red,
Color.blue,
Color.green,Color.black,Color.cyan};
publicMaFenetre()
{setTitle("Ardoisemagique");
setSize(400,180);
Containercontenu=getContentPane();
pan=newPanneau();
pan.addMouseListener(pan);
contenu.add(pan);
boutNouv=newJButton("Nouvelleligne");
contenu.add(boutNouv,"North");
boutNouv.addActionListener(this);
boutEff=newJButton("Effacer");
contenu.add(boutEff,"South");
boutEff.addActionListener(this);
boutCoul=newJButton("");
contenu.add(boutCoul,"West");
boutCoul.addActionListener(this);
boutCoul.setBackground(couleurs[numCoul]);
pan.setCoul(couleurs[numCoul]);
}
publicvoidactionPerformed(ActionEvente)
{if(e.getSource()==boutCoul)
{numCoul++;
if(numCoul>=couleurs.length)numCoul=0;
boutCoul.setBackground(couleurs[numCoul]);
273
pan.setCoul(couleurs[numCoul]);
}
if(e.getSource()==boutNouv)
{pan.nouvelleLigne();
}
if(e.getSource()==boutEff)
{pan.efface();
}
}
privatePanneaupan;
privateJButtonboutNouv,boutEff,boutCoul;
privateintnumCoul=0;
}
classPanneauextendsJPanelimplementsMouseListener
{publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
enCours=false;
}
publicvoidsetCoul(Colorcouleur)
{this.couleur=couleur;
}
publicvoidnouvelleLigne()
{enCours=false;
}
publicvoidefface()
{repaint();
}
publicvoidmouseClicked(MouseEvente)
{intxFin=e.getX();yFin=e.getY();
if(enCours){Graphicsg=getGraphics();
g.setColor(couleur);
g.drawLine(xDeb,yDeb,xFin,yFin);
g.dispose();
}
xDeb=xFin;yDeb=yFin;
enCours=true;
}
publicvoidmousePressed(MouseEvente){}
publicvoidmouseReleased(MouseEvente){}
274
publicvoidmouseEntered(MouseEvente){}
publicvoidmouseExited(MouseEvente){}
privatebooleanenCours=false;
privateintxDeb,yDeb,xFin,yFin;
privateColorcouleur;
}
publicclassArdMag
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
1.Enrevanche,ungestionnairede typeFlowLayout exploiterait la tailledecepanneauqui,pardéfaut, est trèspetite.IlfaudraitalorsrecouriràsetPreferredSize.2.Letracédurectanglepourraits’effectuerplusfacilementavecdrawRect.3.NousaurionspuégalementutiliserunconstructeurJButtonsansarguments.
275
Chapitre10
LesprincipauxcontrôlesdeSwing
Connaissancesrequises
•Casesàcocher(JCheckBox)etboutonsradio(JRadioButton);construction;événementsgénérés:ActionetItem(méthodeitemStateChanged);méthodesisSelectedetsetSelected;groupesdeboutonsradio(ButtonGroup)
•Étiquettes(JLabel);construction;modificationdelibellé(setText)
•Champsdetexte(JTextField);construction;méthodesgetText,setEditableetsetColumns;événementsgénérés:ActionetFocus(méthodesfocusGainedetfocusLost);exploitationfine(interfaceDocumentListener,méthodesinsertUpdate,removeUpdateetchangedUpdate)
•Boîtesdeliste(JList);constructionetchoixdutypedesélection(simple,multiple,intervalle);méthodesgetSelectedValue,getSelectedValues,getSelectedIndexetgetSelectedIndices;événementsgénérés:ListSelection(méthodesvalueChangedetgetValueIsAdjusting)
•Boîtecombo(JComboBox);construction;méthodessetEditableetgetSelectedIndex;événementsgénérés:Action,Item(méthodeitemStateChanged),Focus(méthodesfocusGainedetfocusLost);évolutiondynamique:addItem,addItemAtetremoveItem
Note:lesboutons(JButton)ontfaitl’objetduChapitre8.
276
112Casesàcocher
ÉcrireunprogrammequiaffichedeuxboutonsmarquésRAZetEtatettroiscasesàcocher,delafaçonsuivante:
L’action sur le bouton Etat provoquera l’affichage en fenêtre console des casessélectionnées.L’actionsurRAZremettralestroiscasesàl’étatnoncoché.Enfin,onsignalera en fenêtre console les événements de type Action et Item associés àchacunedestroiscases(enprécisantlasourceconcernée).
Nousplaceronslestroiscasesdansunpanneauassociéàlafenêtre.Nousfaisonsdelafenêtre l’écouteur des boutons et des cases. Comme l’impose l’énoncé, nousredéfinissonsàlafoislesméthodesactionPerformedetitemStateChanged.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
class MaFenetre extends JFrame implements ActionListener,
ItemListener
{publicMaFenetre()
{setTitle("Casesacocher");
setSize(300,140);
Containercontenu=getContentPane();
//lesdeuxboutons
boutRaz=newJButton("RAZ");
277
boutRaz.addActionListener(this);
contenu.add(boutRaz,"North");
boutEtat=newJButton("Etat");
boutEtat.addActionListener(this);
contenu.add(boutEtat,"South");
//lescasesacocherdansunpanneau
pan=newJPanel();
contenu.add(pan);
cercle=newJCheckBox("Cercle");
pan.add(cercle);
cercle.addActionListener(this);
cercle.addItemListener(this);
rectangle=newJCheckBox("Rectangle");
pan.add(rectangle);
rectangle.addActionListener(this);
rectangle.addItemListener(this);
triangle=newJCheckBox("Triangle");
pan.add(triangle);
triangle.addActionListener(this);
triangle.addItemListener(this);
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
if(source==boutRaz)
{cercle.setSelected(false);
rectangle.setSelected(false);
triangle.setSelected(false);
}
if(source==boutEtat)
{System.out.print("Casesselectionnees:");
if(cercle.isSelected())System.out.print("cercle");
if(rectangle.isSelected())System.out.print("rectangle");
if(triangle.isSelected())System.out.print("triangle");
System.out.println();
}
if(source==cercle)System.out.println("Actioncasecercle");
if (source == rectangle) System.out.println ("Action case
rectangle");
278
if (source == triangle) System.out.println ("Action case
triangle");
}
publicvoiditemStateChanged(ItemEvente)
{Objectsource=e.getSource();
if(source==cercle)System.out.println("Itemcasecercle");
if (source == rectangle) System.out.println ("Item case
rectangle");
if (source == triangle) System.out.println ("Item case
triangle");
}
privateJButtonboutRaz,boutEtat;
privateJPanelpan;
privateJCheckBoxcercle,rectangle,triangle;
}
publicclassCoches
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
Itemcasecercle
Actioncasecercle
Itemcaserectangle
Actioncaserectangle
Casesselectionnees:cerclerectangle
Itemcasecercle
Itemcaserectangle
Itemcasetriangle
Itemcasecercle
Actioncasecercle
Itemcaserectangle
Actioncaserectangle
Casesselectionnees:cerclerectangle
On notera qu’à chaque événement Action relatif à une case à cocher correspondtoujoursunévénement Item.La réciproqueest faussepuisqu’unévénement Item peutêtregénérésuiteàunemodificationparprogrammedel’étatd’unecase;danscecas,ellenegénèrepasd’événementAction.
279
Plusieursinstructionssemblablesdoiventêtreécritespourchaquecaseàcocher.Silenombre de cases devenait important, cela pourrait s’avérer fastidieux. Il serait alorspréférable de s’acheminer vers une solution plus concise telle que l’écriture deméthodes (statiques) d’intérêt général, par exemple pour l’ajout d’une case deréférencedonnéeà la fenêtre.Onpourrait aussi conserverdans la fenêtreun tableaudes références des cases ainsi qu’un tableau de chaînes correspondant à leurslibellés…
280
113Casesàcocherennombrequelconque
Généraliserleprogrammedel’exercice105,demanièrequelenombredecasesàcocher puisse être quelconque et déterminé lors de l’appel du constructeur de lafenêtre, auquel on fournira un tableaude chaînes contenant les libellés à associerauxcases:
Lesmessagesenfenêtreconsolecontinuerontderepérerunecaseàcocherparsonlibellé.
Oncontinuenaturellementàplacerlescasesdansunpanneau.Lesdifférentsécouteursrestent lesmêmes.Mais,cettefois,onvaconserver lesréférencesdescasesdansuntableau dont la taille est égale à celle du tableau de chaînes reçu en argument duconstructeurdelafenêtre.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
class MaFenetre extends JFrame implements ActionListener,
ItemListener
{publicMaFenetre(Stringlibelles[])
{setTitle("Casesacocher");setSize(400,160);
Containercontenu=getContentPane();
281
//lesdeuxboutons
boutRaz=newJButton("RAZ");
boutRaz.addActionListener(this);
contenu.add(boutRaz,"North");
boutEtat=newJButton("Etat");
boutEtat.addActionListener(this);
contenu.add(boutEtat,"South");
//lescasesacocherdansunpanneau
pan=newJPanel();contenu.add(pan);
this.libelles=libelles;
nbCases=libelles.length;
cases=newJCheckBox[nbCases];
for(inti=0;i<nbCases;i++)
{cases[i]=newJCheckBox(libelles[i]);
pan.add(cases[i]);
cases[i].addActionListener(this);
cases[i].addItemListener(this);
}
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
if(source==boutRaz)
for(inti=0;i<nbCases;i++)
cases[i].setSelected(false);
if(source==boutEtat)
{System.out.print("Casesselectionnees:");
for(inti=0;i<nbCases;i++)
if(cases[i].isSelected())System.out.print(libelles[i]+"");
System.out.println();
}
for(inti=0;i<nbCases;i++)
if (source == cases[i]) System.out.println ("Action case " +
libelles[i]);
}
publicvoiditemStateChanged(ItemEvente)
{Objectsource=e.getSource();
for(inti=0;i<nbCases;i++)
if (source == cases[i]) System.out.println ("Item case " +
libelles[i]);
}
282
privateJButtonboutRaz,boutEtat;
privateJPanelpan;
privateJCheckBoxcases[];
privateStringlibelles[];
privateintnbCases;
}
publicclassCochesb
{publicstaticvoidmain(Stringargs[])
{ String libelles[] = {"Cercle", "Rectangle", "Triangle",
"Pentagone",
"Ellipse","Carre"};
MaFenetrefen=newMaFenetre(libelles);
fen.setVisible(true);
}
}
283
114Boutonsradioennombrequelconque
Écrire un programme qui affiche un boutonmarquésEtat et un (seul) groupe deboutonsradiodelafaçonsuivante:
Les libellés des boutons radio seront fournis en argument du constructeur de lafenêtre.L’action sur le bouton Etat provoquera l’affichage en fenêtre console du libelléassociéauboutonradiosélectionné.OnsignaleraenfenêtreconsolelesévénementsdetypeActionassociés.
Onpeutfacilementadapterleprogrammedel’exercice106,enremplaçantlesobjetsdetypeJCheckBoxpardesobjetsdetypeJRadioButtonetensupprimantl’écoutedesévénements Item. Il faut simplement prendre soin de rattacher les différents boutonsradioàungroupe(objetdetypeButtonGroup),afind’obtenirlecomportementattendud’ungroupe:lasélectiond’undesboutonsdugroupedésactivetouslesautres.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrameimplementsActionListener
{publicMaFenetre(String[]libelles)
284
{setTitle("Boutonsradio");
setSize(400,160);
Containercontenu=getContentPane();
boutEtat=newJButton("Etat");
boutEtat.addActionListener(this);
contenu.add(boutEtat,"South");
//lesboutonsradiodansunpanneau
pan=newJPanel();
contenu.add(pan);
this.libelles=libelles;
nbBoutons=libelles.length;
ButtonGroupgroupe=newButtonGroup();
boutons=newJRadioButton[nbBoutons];
for(inti=0;i<nbBoutons;i++)
{boutons[i]=newJRadioButton(libelles[i]);
pan.add(boutons[i]);
groupe.add(boutons[i]);
boutons[i].addActionListener(this);
}
if(nbBoutons>0)boutons[0].setSelected(true);
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
if(source==boutEtat)
{System.out.print("Boutonselectionne=");
for(inti=0;i<nbBoutons;i++)
if (boutons[i].isSelected()) System.out.print (libelles[i]+ "
");
System.out.println();
}
for(inti=0;i<nbBoutons;i++)
if(source==boutons[i])
System.out.println("Actionbouton"+libelles[i]);
}
privateJButtonboutDef,boutEtat;
privateJPanelpan;
privateJRadioButtonboutons[];
privateStringlibelles[];
privateintnbBoutons;
285
}
publicclassRadios
{publicstaticvoidmain(Stringargs[])
{ String libelles[] = {"Cercle", "Rectangle", "Triangle",
"Pentagone",
"Ellipse","Carre"};
MaFenetrefen=newMaFenetre(libelles);
fen.setVisible(true);
}
}
ActionboutonTriangle
ActionboutonCarre
Boutonselectionne=Carre
ActionboutonPentagone
ActionboutonRectangle
Boutonselectionne=Rectangle
ActionboutonCercle
Notez que nous avons pris soin de sélectionner initialement le premier bouton dugroupe(ennousassurantqueladimensiondutableaudelibellésétaitnonnulle).
Ici,riennemontreàl’utilisateurquenosboutonsradiofontpartied’unmêmegroupe.Dansunprogrammeréel,onserasouventamenéàmettreenévidenceungroupeenleplaçantdansunpanneauqu’onpourracolorerdifféremmentdu restede la fenêtreouencoredoterd’une"bordure"àl’aidedelaméthodesetBorder.
286
115Champsdetexte
Écrireunprogrammequipermetà l’utilisateurdesaisirunnombreentierdansunchamptexteetquienaffichelecarrélorsqu’ilagitsurunboutonmarquéCALCUL:
Leprogrammedevragérerconvenablementlecasoùl’utilisateurentreautrechosequ’un nombre dans le champ texte ; il pourra par exemple remettre ce champ àblanc.
Ici,nouspouvonsnouspermettred’introduiredirectementdanslafenêtrelesdifférentscontrôles dont nous avons besoin. Nous remplaçons simplement le gestionnaire pardéfautparungestionnairedetypeFlowLayout.NousutilisonsdesobjetsdetypeJLabelpourleslibellés,ainsiquepourlavaleurducarré.LasaisiedunombresefaitdansunobjetnomménombredetypeJTextField.Ici,nousn’avonspasànouspréoccuperdesévénementsgénérésparnombrepuisquelecalculproprementdit estdéclenchéparuneactionextérieure à l’objet.En revanche,nousdevons traiter les événementsde typeAction déclenchés par le bouton.Nous yrécupéronslecontenuduchamptextequenousconvertissonsenentieraveclaméthodeInteger.parseInt.Celle-ci déclenche une exceptionNumberFormatException lorsquelachaînenecorrespondpasàunnombreentier(ycomprislorsqu’ellecontienttropdechiffres). Dans le gestionnaire d’exception correspondant, nous nous contentons deremettreàblanclecontenuduchamptexte.Ici,nouscalculonslecarrédunombredansletypelong,cequiévitetoutproblèmededépassementdecapacité.
importjava.awt.*;
importjava.awt.event.*;
287
importjavax.swing.*;
classMaFenetreextendsJFrameimplementsActionListener
{publicMaFenetre()
{setTitle("Carres");
setSize(400,100);
Containercontenu=getContentPane();
contenu.setLayout(newFlowLayout());
labNombre=newJLabel(etiqNombre);
contenu.add(labNombre);
nombre=newJTextField(10);
contenu.add(nombre);
boutonCalcul=newJButton("CALCUL");
contenu.add(boutonCalcul);
boutonCalcul.addActionListener(this);
labCarre=newJLabel(etiqCarre);
contenu.add(labCarre);
}
publicvoidactionPerformed(ActionEvente)
{if(e.getSource()==boutonCalcul)
try
{Stringtexte=nombre.getText();
intn=Integer.parseInt(texte);
longcarre=(long)n*(long)n;
labCarre.setText(etiqCarre+carre);
}
catch(NumberFormatExceptionex)
{nombre.setText("");
labCarre.setText(etiqCarre);
}
}
privateJLabellabNombre,labCarre;
privateJTextFieldnombre;
static private String etiqNombre = "Nombre : ", etiqCarre =
"Carre:";
privateJButtonboutonCalcul;
}
publicclassCarre
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
288
fen.setVisible(true);
}
}
289
116ChampdetexteetévénementsActionetFocus
Adapter le programmede l’exercice 108 en supprimant le boutonCALCULet demanièreque lecarrédunombres’affiche lorsquel’utilisateurvalide l’informationsaisieoulorsquelechampdetexteperdlefocus:
Il suffit que les actions précédemment réalisées dans l’écouteur du bouton soienttransposées:
•dansl’écouteurdel’événementfocusLostassociéauchampdetexte,
•dansl’écouteurdel’événementActionassociéàcemêmechampdetexte.Pouréviterdedupliquerlesinstructionscorrespondantes,nousprévoyonsuneméthodedeservicenomméeactualise.
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
class MaFenetre extends JFrame implements ActionListener,
FocusListener
{publicMaFenetre()
{setTitle("Carres");
setSize(400,100);
Containercontenu=getContentPane();
contenu.setLayout(newFlowLayout());
labNombre=newJLabel(etiqNombre);
290
contenu.add(labNombre);
nombre=newJTextField(10);
contenu.add(nombre);
nombre.addFocusListener(this);//pourlapertedefocus
nombre.addActionListener(this);//pourlavalidation
labCarre=newJLabel(etiqCarre);
contenu.add(labCarre);
}
publicvoidactionPerformed(ActionEvente)
{actualise();
}
publicvoidfocusLost(FocusEvente)
{actualise();
}
publicvoidfocusGained(FocusEvente)
{
}
publicvoidactualise()
{try
{Stringtexte=nombre.getText();
intn=Integer.parseInt(texte);
longcarre=(long)n*(long)n;
labCarre.setText(etiqCarre+carre);
}
catch(NumberFormatExceptionex)
{nombre.setText("");
labCarre.setText(etiqCarre);
}
}
privateJLabellabNombre,labCarre;
privateJTextFieldnombre;
static private String etiqNombre = "Nombre : ", etiqCarre =
"Carre:";
privateJButtonboutonCalcul;
}
publicclassCarre1
291
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
Comme notre fenêtre ne comporte qu’un seul composant susceptible de recevoir lefocus, leseulmoyendefaireperdre le focusauchampde texteconsiste icià rendreactiveuneautrefenêtre.
292
117Écoutepermanented’unchampdetexte
Adapter le programmede l’exercice 108 en supprimant le boutonCALCULet demanièrequelecarrédunombres’afficheenpermanence,indépendammentdetoutevalidationoudetransfertdefocus:
Ainsi,ici(sil’utilisateurn’apasfaitdecorrectionsaucoursdelafrappe),onverras’affichersuccessivementlescarrésde1,de11,de111…etenfinde11111.
Cettefois,ilfautsavoirquepourimplémenterunobjetdetypeJTextField,Javautiliseàlafoisunobjetdit"document"(detypeDocument)pouryconserverl’informationetunobjetdit "vue"pouren fournir la représentationvisuelle.Toutemodificationd’unobjet de typeDocument génére un des événements de la catégorieDocument qu’ontraite à l’aide d’un écouteur implémentant l’interface DocumentListener. Celle-cicomporte troisméthodes insertUpdate,removeUpdate etchangedUpdate. Seules lesdeuxpremièressontconcernéesparunchampdetexte.L’objetdocumentassociéàuncomposants’obtientparlaméthodegetDocument.Il nous faut donc transposer dans ces deux méthodes les actions précédemmentréalisées dans l’écouteur du bouton de l’exercice. Pour éviter de dupliquer lesinstructions correspondantes, nous prévoyons une méthode de service nomméeactualise.Cettefois,cependant,encasd’exception,nousévitonsderemettreàblanclecontenuduchampdetexte.Eneffet,unetellemodificationrisqueraitdeprovoqueruneboucleinfinie et elle est interdite par Java (elle provoque une exception). Nous nouscontentonsd’effacerlavaleuraffichéecommecarré.
importjava.awt.*;
293
importjava.awt.event.*;
importjavax.swing.*;
importjavax.swing.event.*;//utilepourDocumentListener
classMaFenetreextendsJFrameimplementsDocumentListener
{publicMaFenetre()
{setTitle("Carres");
setSize(400,100);
Containercontenu=getContentPane();
contenu.setLayout(newFlowLayout());
labNombre=newJLabel(etiqNombre);
contenu.add(labNombre);
nombre=newJTextField(10);
contenu.add(nombre);
nombre.getDocument().addDocumentListener(this);
labCarre=newJLabel(etiqCarre);
contenu.add(labCarre);
}
publicvoidinsertUpdate(DocumentEvente)
{actualise();
}
publicvoidremoveUpdate(DocumentEvente)
{actualise();
}
publicvoidchangedUpdate(DocumentEvente)
{
}
publicvoidactualise()
{try
{Stringtexte=nombre.getText();
intn=Integer.parseInt(texte);
longcarre=(long)n*(long)n;
labCarre.setText(etiqCarre+carre);
}
catch(NumberFormatExceptionex)
{//nombre.setText("");generaituneexception
labCarre.setText(etiqCarre);
294
}
}
privateJLabellabNombre,labCarre;
privateJTextFieldnombre;
static private String etiqNombre = "Nombre : ", etiqCarre =
"Carre:";
privateJButtonboutonCalcul;
}
publicclassCarre2
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
295
118Synthèse:sérieharmonique
Écrire un programme permettant d’afficher la somme partielle de la sérieharmonique:
s=1+1/2+1/3+1/4+…+1/nLavaleurdenserainitialiséeà0(onconviendraalorsquesvaut0)etdeuxboutonsmarquésN++etN--permettrontdelafaireévoluer:
Nous conservons le gestionnaire par défaut de la fenêtre, ce qui nous permettra dedisposerleboutonN++avecl’option"North"etleboutonN--avecl’option"South".Au centre de la fenêtre, nous plaçons un panneau dans lequel nous disposons deuxétiquettes(JLabel)quiservirontàafficherlesinformationsvoulues.Lesactionssurlesboutonssontgéréesdanslafenêtreetellesconduisentàl’actualisationdesvaleursdenetdelasommecorrespondante.
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
classMaFenetreextendsJFrameimplementsActionListener
{privatestaticStringtexteN="n=";
privatestaticStringtexteSomme="Serie=";
publicMaFenetre()
{setTitle("Serieharmonique");
296
setSize(200,150);
Containercontenu=getContentPane();
pan=newJPanel();
contenu.add(pan);
boutPlus=newJButton("N++");
boutPlus.addActionListener(this);
contenu.add(boutPlus,"North");
boutMoins=newJButton("N--");
boutMoins.addActionListener(this);
contenu.add(boutMoins,"South");
n=0;
somme=0.;
valeurN=newJLabel(texteN+n+"");
pan.add(valeurN);
valeurSomme=newJLabel(texteSomme+somme);
pan.add(valeurSomme);
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
if(source==boutPlus){n++;
somme+=1./n;
}
if(source==boutMoins&&n>0){somme-=1./n;
n--;
}
valeurN.setText(texteN+n+"");
valeurSomme.setText(texteSomme+somme);
}
privateJPanelpan;
privateJButtonboutPlus,boutMoins;
privateJLabelvaleurN,valeurSomme;
privateintn;
privatedoublesomme;
}
publicclassSerie
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
297
fen.setVisible(true);
}
}
Ici,nousavonsactualisélavaleurdelasommeenluiajoutantouenluisoustrayantlavaleur 1/n. Dès lors que l’utilisateur incrémente et décrémente la valeur de n àdiversesreprises,cemodedecalculconduitàuncumuldeserreurs.Pourl’éviter,onpourrait recalculer entièrement la valeur de la somme à chaque action sur l’un desboutons.
298
119Gestiond’uneboîtedeliste
Écrireunprogrammeaffichantdansunefenêtredesboutonsdontlesétiquettessontdes noms de langage sélectionnés dans une boîte de liste. La liste permettra desélectionner un nombre quelconque de plages de valeurs. Les noms des langagesseront fixés dans la méthodemain (et non dans la fenêtre). On proposera deuxsolutions:
•uneoùlasélectionseravalidéeparl’actionsurunboutonOK:
• une où les boutons affichés dans la fenêtre seront actualisés à chaquemodificationdelasélectiondanslaliste(iln’yauraplusdeboutonOK).
Lesnomsdelangagessontdéfinisparuntableaudechaînesdelaméthodemainqu’onfournit en argument au constructeur de la fenêtre. La boîte de liste est ajoutée à lafenêtreelle-mêmeavecl’option"West".Unpanneauestajoutéaucentredelafenêtre,envued’yafficherlesboutonsvoulus.Le bouton OK est ajouté avec l’option "South" et on gère ses événements de typeAction.LaméthodeactionPerformedréaliselesactionssuivantes:
•suppressiondesboutonsdupanneauparlaméthoderemoveAll(quisupprimetouslescomposantsd’unconteneur);
299
• récupération des valeurs sélectionnées dans la boîte de liste à l’aide degetSelectedValues. Elle fournit un tableau d’éléments de type Object qui serontconvertisenString,avantd’êtretransmisauconstructeurdechacundesboutons;
•appeldelaméthodevalidatedupanneaupourforcerlerecalculparlegestionnairedemiseenforme.importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
classMaFenetreextendsJFrameimplementsActionListener
{publicMaFenetre(Stringnoms[])
{setTitle("Liste");
setSize(300,220);
Containercontenu=getContentPane();
liste=newJList(noms);
contenu.add(liste,"West");
ok=newJButton("OK");
contenu.add(ok,"South");
ok.addActionListener(this);
pan=newJPanel();
contenu.add(pan);
}
publicvoidactionPerformed(ActionEvente)
{if(e.getSource()==ok)
{pan.removeAll();//supprimetouslescomposantsdepan
Objectnoms[]=liste.getSelectedValues();
for(inti=0;i<noms.length;i++)
{JButtonbouton=newJButton((String)noms[i]);
pan.add(bouton);
}
pan.validate();
}
}
privateJListliste;
privateJButtonok;
privateJPanelpan;
}
publicclassListe
{publicstaticvoidmain(Stringargs[])
300
{String[]nomsLangages={"Java","C","C++","Pascal","Basic",
"Cobol",
"Fortran"};
MaFenetrefen=newMaFenetre(nomsLangages);
fen.setVisible(true);
}
}
OnsupprimeleboutonOKetonassocieàlaboîtedelisteunécouteur(icilafenêtre)implémentantl’interfaceListSelectionListener:
liste.addListSelectionListener(this);
L’interface ListSelectionListener comporte une seule méthode valueChanged. Lesévénements correspondants sontgénérésplus souventqu’il n’est nécessairepourunegestion usuelle de la boîte. Il est préférable de faire appel à la méthodegetValueIsAdjustingde laclasseListSelectionEvent, afind’éviter lesévénementsdetransition.VoicicommentpourraitseprésenterlaméthodevalueChanged:
publicvoidvalueChanged(ListSelectionEvente)
{if((e.getSource()==liste)&&(!e.getValueIsAdjusting()))
{pan.removeAll();//supprimetouslescomposantsdepan
Objectnoms[]=liste.getSelectedValues();
for(inti=0;i<noms.length;i++)
{JButtonbouton=newJButton((String)noms[i]);
pan.add(bouton);
}
pan.validate();
}
}
LeprogrammecompletainsiadaptéfiguresurlesiteWebd’accompagnementsouslenomListe1.java.
Rappelonsque,pardéfaut,unboîtedelisteautoriselasélectiondeplusieursplagesdevaleurs.Onaaffaireau typeMULTIPLE_INTERVAL_SELECTION.Onpeut imposerunautretypeàl’aidedelaméthodesetSelectionMode.D’autre part, une boîte de liste ne dispose pas de barre de défilement. Si celle-ci
301
s’avère nécessaire, il faut alors introduire la boîte de liste dans un "panneau dedéfilement"(JScroll-Panel)etdéfinirlenombredevaleursvisiblesàunmomentdonnéparsetVisibleRowCount.
302
120Synthèse:pendule
Afficherunependuleindiquantl’heurefournieparlebiaisdedeuxchampsdetexte(etvalidéeparunbouton"Miseàl’heure"):
Lapenduleseradessinéesurunfonddecouleurjauneetons’arrangerapourqu’ellesoit laplusgrandepossible(toutenétantentièrementvisible)entenantcomptedufait que l’utilisateur peut modifier les dimensions de la fenêtre. On utilisera laméthode drawOval (int abscisse, int ordonnee, int largeur, int hauteur) pourdessineruncercle.
Onutilisedeuxpanneaux:unpourleschampsdetexteetlebouton,unpourledessindelapendule.Ilssontdisposésdanslafenêtreenconservantlegestionnairepardéfaut.Lestroiscontrôlessontécoutésparlafenêtreelle-même.Pour que la pendule s’ajuste à une éventuelle modification de la fenêtre, il estpréférabledeladessinerdanslaméthodepaintComponentdupanneaudanslequelellesetrouve.Ilfautdonccréeruneclassespécialisée(nomméeiciPanPendule)dérivéedeJPanel. Il apparaît alorsunbesoindecommunicationdesvaleurs saisiesentre lafenêtreetlepanneau.Pourlerégler,lesvaleurssaisiessontconservéesdanslafenêtrequ’ondotededeuxméthodesd’accèsgetHeuresetgetMinutes.
303
Nousprévoyonspardéfautdesvaleursnullespourl’heure(heuresetminutes).D’autrepart, nous gérons les éventuelles erreurs de saisie de l’utilisateur (valeurs nonnumériquesousimplementincompatibles).Danscecas,nousavonsprévuderedonnersonanciennevaleurauchampdetextecorrespondant.L’actualisationdelapenduleesttoutsimplementdéclenchéeparl’appeldelaméhoderepaintdupanneau,enréponseàuneactionsurlebouton.Nousdessinonsunegrandeaiguilleayantunetailleégaleaurayondelapenduleetunepetiteaiguilleayantlamoitédecettetaille.Pourdessinerlapetiteaiguille,noustenonscomptedufaitqu’ellesedéplacenonseulementenfonctiondunombred’heures,maisaussienfonctiondunombredeminutes.
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
classMaFenetreextendsJFrameimplementsActionListener
{publicMaFenetre()
{setTitle("PENDULE");
setSize(400,250);
Containercontenu=getContentPane();
panControles=newJPanel();
contenu.add(panControles,"North");
saisieHeures=newJTextField(4);
panControles.add(saisieHeures);
etiqHeures=newJLabel("Heures");
panControles.add(etiqHeures);
saisieMinutes=newJTextField(4);
panControles.add(saisieMinutes);
etiqMinutes=newJLabel("Minutes");
panControles.add(etiqMinutes);
ok=newJButton("Miseal'heure");
panControles.add(ok);
ok.addActionListener(this);
panPendule=newPanPendule(this);
contenu.add(panPendule);
panPendule.setBackground(Color.yellow);
}
publicintgetMinutes()
{returnminutes;
}
304
publicintgetHeures()
{returnheures;
}
publicvoidactionPerformed(ActionEvente)
{inth,m;//pourlesvaleurssaisies
if(e.getSource()==ok)
{try
{StringchHeures=saisieHeures.getText();
h=Integer.parseInt(chHeures);
}
catch(NumberFormatExceptionex)
{h=-1;//onforceunevaleurinvalide
saisieHeures.setText("");
}
try
{StringchMinutes=saisieMinutes.getText();
m=Integer.parseInt(chMinutes);
}
catch(NumberFormatExceptionex)
{m=-1;//onforceunevaleurinvalide
saisieMinutes.setText("");
}
//silesvaleursobtenuessontvalides,onlesplacedans
//leschampsheuresetminutesetonforceledessin
//sinon,onreplacelesanciennesvaleursdansleschampstexte
if((h>=0)&&(h<24)&&(m>=0)&&(m<60))
{heures=h;minutes=m;
repaint();
}
else
{saisieMinutes.setText(""+minutes);
saisieHeures.setText(""+heures);
}
}
}
privateJPanelpanControles;
privatePanPendulepanPendule;
privateJTextFieldsaisieHeures,saisieMinutes;
privateJLabeletiqHeures,etiqMinutes;
305
privateJButtonok;
privateintminutes=0,heures=0;
}
classPanPenduleextendsJPanel
{publicPanPendule(MaFenetrefen)
{this.fen=fen;
}
publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
//dessinducercle
Dimensiondim=getSize();
intlargeur=dim.width,hauteur=dim.height;
booleanpanTropLarge=(largeur>hauteur);
intxCentre=largeur/2,yCentre=hauteur/2;
intrayon;
if(panTropLarge)rayon=hauteur/2-2;elserayon=largeur/2-
2;
g.drawOval(xCentre-rayon,yCentre-rayon,2*rayon,2*rayon);
//dessingrandeaiguille
intminutes=fen.getMinutes();
doubleangle=Math.PI/2*(1.-minutes/15.);
g.drawLine(xCentre,yCentre,
(int)(xCentre+rayon*Math.cos(angle)),
(int)(yCentre-rayon*Math.sin(angle)));
//dessinpetiteaiguille
intheures=fen.getHeures();
angle=Math.PI/2*(1.-heures/3.-minutes/180.);
g.drawLine(xCentre,yCentre,
(int)(xCentre+rayon/2.*Math.cos(angle)),
(int)(yCentre-rayon/2.*Math.sin(angle)));
}
privateMaFenetrefen;
}
publicclassPendule
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
306
}
307
Chapitre11
Lesboîtesdedialogue
Connaissancesrequises
•Boîtesdemessage:méthodeshowMessageDialog(choixducontenudumessage,dutitredelaboîte,dutyped’icône)
•Boîtesdeconfirmation:méthodeshowConfirmDialog(choixdutextedelaquestion,dutitredelaboîte,desboutons)
•Boîtesdesaisie:méthodeshowInputDialog(choixdutextedelaquestion,dutitredelaboîte,dutyped’icône)
•Boîtesd’options:méthodeshowInputDialogoushowOptionDialog
•Boîtesdedialoguepersonnalisées:classeJDialog,méthodesetVisible,gestiondudialogue,transfertd’informationentrelaboîtededialogueetsonconteneur
308
121Utilisationdeboîtesdemessageetdeconfirmation
Écrireunprogrammequiaffichelescarrésdesnombresimpairsàpartirde1.Aprèsl’affichagedechaquecarré,ondemanderaàl’utilisateurs’ilsouhaitecontinuer.Onutilisera des boîtes de message et des boîtes de confirmation comme dans lesillustrationsdelapagesuivante:
Ici, il n’est pas nécessaire de créer une fenêtre, de sorte que le programme seraréduitàunesimpleméthodemain.
Nousutilisonsuneboîtedemessagepourafficherchacundescarrés.Commecelle-cin’est rattachée à aucune fenêtre, le premier argument de la méthodeshowMessageDialog est null. Les arguments suivants précisent respectivement lemessage à afficher (ici le nombre impair courant et son carré), le titre de la boîte(CARRES)etletyped’icône(information).Après chaque affichage d’un carré, nous demandons à l’utilisateur s’il souhaitecontinuer en utilisant une boîte de confirmation créée par la méthodeshowConfirmDialog. Là encore le premier argument est null. Les autres précisentrespectivementlaquestionàafficher(impairsuivant?),letitredelaboîteetlanaturedesboutonsfigurantdanslaboîte(YESetNO).Notez que le choix du type d’icône ou celui de la nature des boutons est exprimé àl’aidedeconstantesprédéfiniesdelaclasseJOptionPane,cequiestpluspratiquequed’utiliserlesvaleursentièrescorrespondantes.
importjavax.swing.*;
309
publicclassCarres
{
publicstaticvoidmain(String[]args)
{intn=1;
intrep;
do
{JOptionPane.showMessageDialog(null,n+"apourcarre"+n*n,
"CARRES",JOptionPane.INFORMATION_MESSAGE);
n+=2;
rep=JOptionPane.showConfirmDialog(null,"impairsuivant?",
"CARRES",JOptionPane.YES_NO_OPTION);
}
while(rep==JOptionPane.YES_OPTION);
}
}
310
122Utilisationdeboîtesdemessage,deconfirmationetdesaisie
Écrire un programme qui lit des valeurs flottantes et en affiche la moyenne. Lesvaleursserontluesparl’intermédiaired’uneboîtedesaisie.Uneréponseincorrecteferal’objetd’unmessaged’avertissement(encasd’actionsurleboutonCanceloudefermeturedelaboîte,onredemanderalavaleur).Aprèschaquevaleurcorrectementlue,ondemanderaàl’utilisateurs’ilenad’autresàfournir.Alafin,uneboîtedemessagefourniralenombredevaleursluesetleurmoyenne.Ici,iln’estpasnécessairedecréerunefenêtre(leprogrammeseradoncréduitàunesimpleméthodemain). Voici quelques illustrations du dialogue avec l’utilisateurpourcetexemple:
Nousutiliseronsdonclesboîtesdedialoguestandardconstruitesautomatiquementparles méthodes showMessageDialog, showConfirmDialog et showInputDialog. Lepremier argument de leur appel est toujours null puisque nous ne cherchons pas àrattachercesboîtesàunefenêtreparticulière.Lechoixdesformesd’icôneoudutype
311
des boutons est fait à l’aide des constantes prédéfinies de la classe JOptionPane :JOptionPane.QUESTION_MESSAGE pour l’icône "point d’interrogation",JOptionPane.YES_NO_OPTIONpournedisposerquedesdeuxboutonsYESetNO.Encequiconcernelasaisiedesvaleurs,nousconvertissonslachaînelueenundoubleavec la méthode Double.parseDouble. Nous traitons les valeurs incorrectes eninterceptantl’exceptionNumberFormatExceptionqu’ellegénère.Rappelonsquesil’utilisateurcliquesurleboutonCanceld’uneboîtedesaisieous’illaferme,laméthodeshowInputDialogfournitlavaleurnull.
importjavax.swing.*;
publicclassMoyenne
{
publicstaticvoidmain(String[]args)
{intn=0;
doublex=0,somme=0,moyenne;
intcontinuer;
//lecturedesdifferentesvaleurs
do
{booleanok;
n++;
do//boucledelectured'unevaleurjusqu'acorrecte
{ok=false;
Stringrep=(String)JOptionPane.showInputDialog
(null,"donnezlavaleurderang"+n,
"MOYENNES",JOptionPane.QUESTION_MESSAGE);
if (rep == null) continue ; // si action sur Cancel ou
fermeture
try
{x=Double.parseDouble(rep);
ok=true;
}
catch(NumberFormatExceptione)
{JOptionPane.showMessageDialog(null,"reponseincorrecte");
}
}
while(!ok);
somme+=x;
continuer=JOptionPane.showConfirmDialog
(null,"Avez-vousencoredesvaleurs?",
312
"MOYENNES",JOptionPane.YES_NO_OPTION);
}
while(continuer==JOptionPane.YES_OPTION);
//calculdelamoyenneetaffichage
moyenne=somme/n;
JOptionPane.showMessageDialog
(null,"moyennedes"+n+"valeurs="+moyenne,
"RESULTATS",JOptionPane.INFORMATION_MESSAGE);
}
}
313
123Programmationd’uneboîtedemessage
Sans utiliser les boîtes de dialogue standard, écrire une méthode statiqueafficheMessage(d’uneclassenomméeUtil)affichantuneboîtedemessagecommele fait la méthode JOptionPane.showMessageDialog (qu’on ne devra donc pasutiliser).Poursimplifierleschoses,laboîteaffichéenecomporterapasd’icôneetsera de taille fixe (par exemple 200x100) et son titre sera toujours "MESSAGE"commedanscetexemple:
LaméthodeafficheMessagenecomporteraquedeuxarguments:laréférencedelafenêtre parent (supposée de type JFrame ou dérivé) et le texte du message àafficher.Ecrireunpetitprogrammed’essai.
Comme l’énoncé nous interdit d’utiliser les boîtes de dialogue standard, il estnécessairedecréerunobjetboîtededialogued’uneclasseJDialogoudérivée.Nousvousproposonsdeuxsolutions : l’uneutilisantdirectement laclasseJDialog, l’autrecréantuneclassespécialiséedérivéedeJDialog.
PremièresolutionDans la méthode afficheMessage, nous créons donc un objet de type JDialog, enfournissanttruecommetroisièmeargument,cequicorrespondaucasusueld’uneboîte"modale".Nousyplaçonsdeuxcomposants:unboutonOKetuneétiquette(JLabel).Comme legestionnairepardéfautd’uneboîtededialogueestde typeBorderLayout,nousleremplaçonsparungestionnairedetypeFlowLayout.
314
L’affichage de la boîte est provoqué par l’appel de sa méthode setVisible avecl’argumenttrue.Lafindudialoguedoitêtredéclenchéeparl’écouteurdesévénementsActionassociésauboutonOK. Ici,cetécouteurnepeutêtrequ’unobjetd’uneclassespécifique(nomméeEcoutOK).Nous transmettons la référencede laboîteconcernéeauconstructeur:laméthodeactionPerformedsecontentedeluiappliquerlaméthodesetVisible(false)pourmettrefinaudialogue(aucuntestn’abesoind’êtreréalisédansla méthode afficheMessage). Avant de quitter la méthode afficheMessage, nousprenonssoind’appeler laméthodedisposeafinde libérer laboîtededialogueet lesdifférentsobjetsquiluisontassociés.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classUtil
{staticvoidafficheMessage(JFrameparent,Stringmessage)
{//creationdel'objetboitededialogue
JDialogboiteMessage=newJDialog(parent,"MESSAGE",true);
boiteMessage.setSize(200,100);
//miseenplacedescomposants:boutonOK,etiquette
Containercontenu=boiteMessage.getContentPane();
contenu.setLayout(newFlowLayout());
JLabeltxt=newJLabel(message);
contenu.add(txt);
JButtonok=newJButton("OK");
contenu.add(ok);
ok.addActionListener(newEcouteOK(boiteMessage));
//affichagedudialogue
boiteMessage.setVisible(true);
//finsurOK-rienatesterici
boiteMessage.dispose();
}
}
classEcouteOKimplementsActionListener
{publicEcouteOK(JDialogbd)
{this.bd=bd;
}
publicvoidactionPerformed(ActionEvente)
{bd.setVisible(false);
}
315
privateJDialogbd;
}
publicclassTstMess
{publicstaticvoidmain(Stringargs[])
{JFramefen=newJFrame("EssaiafficheMessage");
fen.setSize(400,300);
fen.setVisible(true);
Util.afficheMessage(fen,"bonjour");
Util.afficheMessage(fen,"etaurevoir");
}
}
Deuxièmesolution
Nous créonsune classeBoiteMessage dérivée de JDialog. Cette fois, l’écouteur duboutonOKpeutêtrel’objetboîtedemessagelui-même,desortequ’aucuneinformationn’abesoind’êtretransmiseàl’écouteur.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classUtil
{staticvoidafficheMessage(JFrameparent,Stringmessage)
{BoiteMessageboiteMessage=newBoiteMessage(parent,message);
boiteMessage.setVisible(true);
boiteMessage.dispose();
}
}
classBoiteMessageextendsJDialogimplementsActionListener
{publicBoiteMessage(JFrameparent,Stringmessage)
{super(parent,"MESSAGE",true);
setSize(200,100);
Containercontenu=getContentPane();
contenu.setLayout(newFlowLayout());
JLabeltxt=newJLabel(message);
contenu.add(txt);
JButtonok=newJButton("OK");
contenu.add(ok);
ok.addActionListener(this);
316
}
publicvoidactionPerformed(ActionEvente)
{setVisible(false);
}
}
publicclassTstMess2
{publicstaticvoidmain(Stringargs[])
{JFramefen=newJFrame("EssaiafficheMessage");
fen.setSize(400,300);
fen.setVisible(true);
Util.afficheMessage(fen,"bonjour");
Util.afficheMessage(fen,"etaurevoir");
}
}
317
124Programmationd’uneboîtedeconfirmation
Sans utiliser les boîtes de dialogue standard, écrire une méthode statiqueafficheConfirme (d’une classe nomméeUtil) affichant une boîte de confirmationcomme le fait la méthode JOptionPane.showConfirmDialog. Pour simplifier leschoses, la boîte affichée ne comportera pas d’icône et sera de taille fixe (parexemple200x100),sontitreseratoujours"CHOIX"etellecomporteratoujourslesboutonsOui,NonetAnnulcommedanscetexemple:
LaméthodeafficheConfirmnecomporteraquedeuxarguments:laréférencedelafenêtreparent (supposéede typeJFrame oudérivé) et le textede laquestion.Savaleurderetour,detypeint,préciseral’actioneffectuée:0pourOui,1pourNon,2pourAnnul,-1pourlafermeturedelaboîte.Écrireunpetitprogrammed’essai.
Ici,nouscréonsunobjetd’uneclassespécialiséeBoiteConfirme(dérivéedeJDialog)dontnousfaisonssonpropreécouteurdesactionssurlesdifférentsboutons.Unchampprivénomméetat estutilisépour identifier leboutonactionnépar l’utilisateur.NousutilisonslesvaleursprévuesenretourdeafficheConfirme.Pouréviterd’avoiràtraiterl’événement"fermeturedelaboîtededialogue",nousplaçonsinitialementcechampàlavaleur-1(valeurderetourprévueencasdefermeturedelaboîte).Une méthode d’accès nommée getEtat permet à la méthode afficheConfirme deconnaîtrelechoixfaitparl’utilisateuraprèslafindudialogue.
importjavax.swing.*;
importjava.awt.*;
318
importjava.awt.event.*;
classBoiteConfirmeextendsJDialogimplementsActionListener
{
publicBoiteConfirme(JFrameparent,Stringmessage)
{super(parent,"CHOIX",true);
setSize(200,100);
//miseenplacedescomposants:boutonOK,étiquette
Containercontenu=getContentPane();
contenu.setLayout(newFlowLayout());
JLabeltxt=newJLabel(message);
contenu.add(txt);
yes=newJButton("Oui");
yes.addActionListener(this);
contenu.add(yes);
no=newJButton("Non");
contenu.add(no);
no.addActionListener(this);
cancel=newJButton("Annul");
contenu.add(cancel);
cancel.addActionListener(this);
}
publicvoidactionPerformed(ActionEvente)
{if(e.getSource()==yes)etat=0;
if(e.getSource()==no)etat=1;
if(e.getSource()==cancel)etat=2;
setVisible(false);
}
publicintgetEtat()
{returnetat;
}
privateJButtonyes,no,cancel;
privateintetat=-1;
}
classUtil
{staticintafficheConfirme(JFrameparent,Stringmessage)
{//creationdel'objetboitededialogue
BoiteConfirmeboiteConf=newBoiteConfirme(parent,message);
319
//affichagedudialogue
boiteConf.setVisible(true);
//findudialogue
boiteConf.dispose();
returnboiteConf.getEtat();
}
}
publicclassTstChoix
{publicstaticvoidmain(Stringargs[])
{JFramefen=newJFrame("EssaiBoiteConfirmation");
fen.setSize(400,300);
fen.setVisible(true);
intrep=Util.afficheConfirme(fen,"Voulez-vouscontinuer?");
System.out.println("reponse="+rep);
}
}
320
125Programmationd’uneboîtedesaisie
Sans utiliser les boîtes de dialogue standard, écrire une méthode statiqueafficheSaisie (d’une classe nommée Util) affichant une boîte de confirmationcomme le fait la méthode JOptionPane.showInputDialog. Pour simplifier leschoses, la boîte affichée ne comportera pas d’icône, elle sera de taille fixe (parexemple240x150),sontitreseratoujours"SAISIE"etellecomporteratoujourslesboutonsOKetAnnulcommedanscetexemple:
LaméthodeafficheSaisie ne comportera que deux arguments : la référence de lafenêtreparent(supposéedetypeJFrameoudérivé)etletexteàafficher.Savaleurderetour,detypeStringseral’informationsaisieoulavaleurnullencasd’actionsurAnnuloudefermeturedelaboîte.Écrireunpetitprogrammed’essai.
Ici, nous créons un objet d’une classe spécialiséeBoiteSaisie (dérivée de JDialog)dont nous faisons son propre écouteur des actions sur les deux boutons qui doiventdéclencher la fin du dialogue. Les événements du champ de texte n’ont pas besoind’êtreprisencomptepuisquelavalidationdel’informationsefaitparleboutonOK.Unchampprivénommé infoLue est utilisépour conserver la chaîne lue.Pour éviterd’avoir à traiter l’événement "fermeture de la boîte de dialogue", nous plaçonsinitialementcechampàlavaleurnull(valeurderetourprévueencasdefermeturedelaboîte).Uneméthoded’accèsnomméegetInfopermetàlaméthodeafficheSaisiedeconnaître
321
lechoixfaitparl’utilisateuraprèslafindudialogue.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classBoiteSaisieextendsJDialogimplementsActionListener
{
publicBoiteSaisie(JFrameparent,Stringmessage)
{super(parent,"SAISIE",true);
setSize(240,150);
//miseenplacedescomposants
Containercontenu=getContentPane();
contenu.setLayout(newFlowLayout());
JLabeltxt=newJLabel(message);
contenu.add(txt);
saisie=newJTextField(20);
contenu.add(saisie);
ok=newJButton("OK");
ok.addActionListener(this);
contenu.add(ok);
cancel=newJButton("Annul");
contenu.add(cancel);
cancel.addActionListener(this);
}
publicvoidactionPerformed(ActionEvente)
{if(e.getSource()==ok)
{infoLue=saisie.getText();
}
setVisible(false);
}
publicStringgetInfo()
{returninfoLue;
}
privateJButtonok,cancel;
privateJTextFieldsaisie;
privateStringinfoLue=null;
}
classUtil
{staticStringafficheSaisie(JFrameparent,Stringmessage)
322
{//creationdel'objetboitededialogue
BoiteSaisieboiteSaisie=newBoiteSaisie(parent,message);
//affichagedudialogue
boiteSaisie.setVisible(true);
//findudialogue
boiteSaisie.dispose();
returnboiteSaisie.getInfo();
}
}
publicclassTstSaisie
{
publicstaticvoidmain(Stringargs[])
{Stringrep;
JFramefen=newJFrame("EssaiBoitedesaisie");
fen.setSize(400,300);
fen.setVisible(true);
do//oninterrogel'utilisateurjusqu'acequ'ilreponde"fin"
{rep=Util.afficheSaisie(fen,"Donnezuntexte?");
if(rep!=null)
System.out.println("reponse="+rep);
}
while((rep==null)||!rep.equals("fin"));
}
}
323
126Synthèse:saisied’uneheure
Réaliser une classe nommée DialogueSaisieHeure permettant à l’utilisateur desaisir, dans une boîte de dialogue, une heure exprimée sous la forme de deuxnombresentiers:unnombred’heurescomprisentre0et23,unnombredeminutescomprisentre0et59.Laboîtededialoguecomporteradeuxchampsdetextepourlasaisiedesentiers,unboutonOKetunboutonAnnul:
Si l’utilisateur agit surOK alors que les valeurs fournies sont incorrectes (nonnumériquesouhorsplage),onluidemanderad’enfournird’autres(onnemettrapasfinaudialogue):
Laclassedisposera:
• d’un constructeur à un argument de type JFrame correspondant à la fenêtreparentàutiliserpourlaboîte;
• d’une méthode lanceDialog permettant d’afficher la boîte et de gérer ledialogue. Elle fournira en retour : la valeur true si le dialogue s’est terminénormalement (valeurscorrectespuisactionsurOK), lavaleur false dans le cascontraire(Annuloufermeturedelaboîte);
• de deux méthodes getHeures et getMinutes permettant de "récupérer" lesvaleurssaisies.
324
Écrireunpetitprogrammedetest.
Iciencore, laboîtededialoguesera sonpropreécouteurdesactionssur lesboutonsOKetAnnul.Iln’estpasnécessaired’écouterleschampsdetextepuisqueleurcontenun’estprisencomptequ’aumomentdel’actionsurOK.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classDialogueSaisieHeureextendsJDialogimplementsActionListener
{publicDialogueSaisieHeure(JFrameparent)
{super(parent,"ENTREZUNEHEURE",true);
this.parent=parent;
setSize(240,120);
//miseenplacedescomposants
Containercontenu=getContentPane();
contenu.setLayout(newFlowLayout());
JLabeletiqHeures=newJLabel("Heures");
contenu.add(etiqHeures);
saisieHeures=newJTextField(3);
contenu.add(saisieHeures);
JLabeletiqMinutes=newJLabel("Minutes");
contenu.add(etiqMinutes);
saisieMinutes=newJTextField(3);
contenu.add(saisieMinutes);
ok=newJButton("OK");
ok.addActionListener(this);
contenu.add(ok);
cancel=newJButton("Annul");
contenu.add(cancel);
cancel.addActionListener(this);
}
publicvoidactionPerformed(ActionEvente)
{if(e.getSource()==ok)
{//recuperationinfossaisies
StringchHeures=saisieHeures.getText();
StringchMinutes=saisieMinutes.getText();
325
//essaideconversionenentiers
try
{heures=Integer.parseInt(chHeures);
minutes=Integer.parseInt(chMinutes);
}
catch(NumberFormatExceptioneX)
{ JOptionPane.showMessageDialog (parent, "Valeurs non
numériques");
saisieHeures.setText("");
saisieMinutes.setText("");
return;
}
//conversionreussie-verificationdesplages
if((heures>=0)&&(heures<24)&&(minutes>=0)&&(minutes<60))
correct=true;
else
{JOptionPane.showMessageDialog(parent,"Valeurshorsplage");
saisieHeures.setText("");
saisieMinutes.setText("");
return;
}
}
setVisible(false);
}
publicintgetHeures()
{returnheures;
}
publicintgetMinutes()
{returnminutes;
}
publicbooleanlanceDialog()
{correct=false;
//affichagedudialogue
setVisible(true);
//findudialogue
dispose();
returncorrect;
}
privateJFrameparent;
326
privateJButtonok,cancel;
privateJTextFieldsaisieHeures,saisieMinutes;
privateintheures,minutes;
privatebooleancorrect;
}
publicclassTstHM
{publicstaticvoidmain(Stringargs[])
{DialogueSaisieHeureboiteHeure;
JFramefen=newJFrame("Essaiboitesaisieheure");
fen.setSize(400,300);
fen.setVisible(true);
boiteHeure=newDialogueSaisieHeure(fen);
if(boiteHeure.lanceDialog()==true)
{System.out.println("Heurefournie:"+boiteHeure.getHeures()
+"h"
+boiteHeure.getMinutes()+"mn");
}
else
System.out.println("dialogueabandonne");
boiteHeure.dispose();
}
}
L’énoncénous imposaitde recouriràuneméthode lanceDialogmembrede laclasseDialogueSaisieHeure et non plus à uneméthode statique. Il est donc nécessaire quel’objetboîtededialogueaitétécrééavantl’appeldecetteméthode.C’estcequenousfaisonsicidanslaméthodemain.Cettedémarcheestpluscontraignantequecellequiconsisteraitàcréerautomatiquementlaboîtelorsdel’appeld’uneméthodestatique.Enrevanche,ellepermettrait,sion lesouhaitait,denecréerqu’uneseulefoisuneboîtequ’onutiliseensuiteàdiversesreprises
327
Chapitre12
Lesmenus
Connaissancesrequises
•Barredemenus(JMenuBar);construction;rattachementàunefenêtre(méthodesetJMenuBar)
•Objetsmenus(JMenu);construction;ajoutàunebarredemenus;événementsgénérés(MenuEvent),méthodesmenuSelected,menuDeselected,menuCanceled;ajoutd’unebarreséparatrice(méthodeaddSeparator)
•Optionsdemenus(JMenuItem);construction;ajoutàunmenu;événementsActiongénérés
•Optionscaseàcocher(JCheckBoxMenuItem);optionsboutonsradio(JRadiButtonMenuItem);groupedeboutonsradio
•Menussurgissants(JPopupMenu)
•Compositiond’optionsdemenu
•Menusdynamiques;activation/désactivationd’options(setEnabled)
328
127Créationd’unmenudéroulantusuel
Créerunefenêtre(dérivéedeJFrame)munied’unebarredemenusconstituée:
•d’unmenuFichiercomportantlesoptions:Ouvrir,SauvegarderetFermer,
•d’unmenuEditioncomportantlesoptions:CopieretColler.
Onnechercherapasiciàtraiterlesactionscorrespondantes.
Dansleconstructeurdelafenêtre,nouscréonsunobjetdetypeJMenuBaretnouslerattachons à la fenêtre avec la méthode add. Puis nous créons deux objets de typeJMenu (nommés fichier et edition) que nous rattachons à la barre desmenus. Pourchaque menu, nous créons les options voulues (de type JMenuItem) et nous lesassocionsaumenuparadd.
importjava.awt.*;
importjavax.swing.*;
classFenMenuextendsJFrame
{publicFenMenu()
{setTitle("Exempledemenus");
setSize(300,120);
/*creationbarredesmenus*/
barreMenus=newJMenuBar();
setJMenuBar(barreMenus);
329
/*creationmenuFichieretsesoptions*/
fichier=newJMenu("Fichier");
barreMenus.add(fichier);
ouvrir=newJMenuItem("Ouvrir");
fichier.add(ouvrir);
sauvegarder=newJMenuItem("Sauvegarder");
fichier.add(sauvegarder);
fermer=newJMenuItem("Fermer");
fichier.add(fermer);
/*creationmenuEditionetsesoptions*/
edition=newJMenu("Edition");
barreMenus.add(edition);
copier=newJMenuItem("Copier");
edition.add(copier);
coller=newJMenuItem("Coller");
edition.add(coller);
}
privateJMenuBarbarreMenus;
privateJMenufichier,edition;
privateJMenuItemouvrir,sauvegarder,fermer,copier,coller;
}
publicclassFiched1
{publicstaticvoidmain(Stringargs[])
{FenMenufen=newFenMenu();
fen.setVisible(true);
}
}
Dans la précédente solution, le constructeur de la fenêtre comportait plusieursinstructions semblables, notamment la création d’une option et son rattachement aumenu. Si le nombre d’options de chaquemenu devenait important, on pourrait avoirintérêtàécrireuneméthodestatiqueregroupantcesdifférentestâchescommedanscetexemple(oùcetteméthodesenommeajoute):
importjava.awt.*;
importjavax.swing.*;
classFenMenuextendsJFrame
330
{publicFenMenu()
{setTitle("Exempledemenus");
setSize(300,150);
/*creationbarredesmenus*/
barreMenus=newJMenuBar();
setJMenuBar(barreMenus);
/*creationmenuFichieretsesoptions*/
fichier=newJMenu("Fichier");
barreMenus.add(fichier);
ouvrir=ajoute("Ouvrir",fichier);
sauvegarder=ajoute("Sauvegarder",fichier);
fermer=ajoute("Fermer",fichier);
/*creationmenuEditionetsesoptions*/
edition=newJMenu("Edition");
barreMenus.add(edition);
copier=ajoute("Copier",edition);
coller=ajoute("Coller",edition);
}
privatestaticJMenuItemajoute(Stringlibelle,JMenumenu)
{JMenuItemoption=newJMenuItem(libelle);
menu.add(option);
returnoption;
}
privateJMenuBarbarreMenus;
privateJMenufichier,edition;
privateJMenuItemouvrir,sauvegarder,fermer,copier,coller;
}
publicclassFichedb1
{publicstaticvoidmain(Stringargs[])
{FenMenufen=newFenMenu();
fen.setVisible(true);
}
}
Ilestnécessairequelaméthodeajouterenvoielaréférenceàl’optionqu’elleacréée.Iln’estpaspossibledel’écrireparexempledecettemanière:
331
private static void ajoute (String libelle, JMenu menu, JMenuItem
option)
{option=newJMenuItem(libelle);
menu.add(option);
}
Onpeutégalementenvisagerd’employerdestableauxd’optionsetdelibelléscommedanscetexemple:
importjava.awt.*;
importjavax.swing.*;
classFenMenuextendsJFrame
{publicFenMenu()
{setTitle("Exempledemenus");
setSize(300,120);
/*creationbarredesmenus*/
barreMenus=newJMenuBar();
setJMenuBar(barreMenus);
/*creationmenuFichieretsesoptions*/
fichier=newJMenu("Fichier");
barreMenus.add(fichier);
intnOptionsFichier=nomsOptionsFichier.length;
optionsFichier=newJMenuItem[nOptionsFichier];
for(inti=0;i<nOptionsFichier;i++)
{optionsFichier[i]=newJMenuItem(nomsOptionsFichier[i]);
fichier.add(optionsFichier[i]);
}
/*creationmenuEditionetsesoptions*/
edition=newJMenu("Edition");
barreMenus.add(edition);
intnOptionsEdition=nomsOptionsEdition.length;
optionsEdition=newJMenuItem[nOptionsEdition];
for(inti=0;i<nOptionsEdition;i++)
{optionsEdition[i]=newJMenuItem(nomsOptionsEdition[i]);
edition.add(optionsEdition[i]);
}
}
332
privateJMenuBarbarreMenus;
privateJMenufichier,edition;
privateJMenuItem[]optionsFichier,optionsEdition;
private String[] nomsOptionsFichier = {"Ouvrir", "Sauvegarder",
"Fermer"};
privateString[]nomsOptionsEdition={"Copier","Coller"};
}
publicclassFiched1a
{publicstaticvoidmain(Stringargs[])
{FenMenufen=newFenMenu();
fen.setVisible(true);
}
}
Cettedémarcheneseracependantpastoujoursfacileàassocieravecletraitementdesévénementsgénérésparlesdifférentesoptions(nondemandéici).
333
128Gestiondesactionssurlesoptionsd’unmenu
On se propose de traiter les actions sur les options des deux menus créés dansl’exercice 120. On ne cherchera pas à manipuler véritablement un fichier maisseulementunnomdefichierfourniparuneboîtedesaisiedéclenchéeparl’optionOuvrir.On "tracera" en fenêtre console les opérations résultant desdifférentes actionsdel’utilisateurcommedanscetexemple:
Onouvretruc
copied'information
onsauvegardetruc
collaged'information
Rienacoller
onsauvegardetruc
Onfermetruc
Onouvrechose
Onfermechose
Pasdefichierouvertasauvegarder
pasdefichierouvert
PourlesoptionsdumenuFichier,onpréciseralanaturedel’opération(ouverture,sauvegarde, fermeture)et lenomdufichierconcerné.Onsupposeraqu’onnepeutouvrirqu’unseulfichieràlafoisetquel’ouvertured’unnouveaufichierentraînelafermeturedel’ancien(unemêmeoptionpeutdoncdéclencherplusieursopérations).Onsignalera les choix incohérents telleunedemandede fermeturealorsqu’aucunfichiern’estouvert.PourlesoptionsdumenuEdition,onsecontenteradepréciserl’opérationréaliséeetdesignalerleschoixincohérents.Onsupposeraqu’unemêmeinformationnepeutêtrecolléequ’uneseulefois.
IlnoussuffitdetraiterlesévénementsActiondéclenchésparlesdifférentesoptionsdesmenus.Ici,nousfaisonsdelafenêtresonpropreécouteur.
334
En réponse à l’optionOuvrir, nous demandons à l’utilisateur de fournir un nom defichier dans une boîte de saisie standard.Nous tenons compte du fait qu’il peut trèsbien abandonner la saisie (fermeture de la boîte ou action surCancel) et donc nefourniraucunnom.Demême,nousconsidéronsqu’unnomviden’estpasuneréponsesatisfaisante.Des indicateurs booléens fichierOuvert et infoCopiee nous permettent de suivrel’évolutiondelasituation.
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
importjavax.swing.event.*;
classFenMenuextendsJFrameimplementsActionListener
{publicFenMenu()
{setTitle("Exempledemenus");
setSize(300,130);
/*creationbarredesmenus*/
barreMenus=newJMenuBar();
setJMenuBar(barreMenus);
/*creationmenuFichieretsesoptions*/
fichier=newJMenu("Fichier");
barreMenus.add(fichier);
ouvrir=newJMenuItem("Ouvrir");
fichier.add(ouvrir);
ouvrir.addActionListener(this);
sauvegarder=newJMenuItem("Sauvegarder");
fichier.add(sauvegarder);
sauvegarder.addActionListener(this);
fermer=newJMenuItem("Fermer");
fichier.add(fermer);
fermer.addActionListener(this);
/*creationmenuEditionetsesoptions*/
edition=newJMenu("Edition");
barreMenus.add(edition);
copier=newJMenuItem("Copier");
edition.add(copier);
copier.addActionListener(this);
coller=newJMenuItem("Coller");
edition.add(coller);
335
coller.addActionListener(this);
/*etatinitial:pasdefichierouvert,pasd'infocopiee*/
fichierOuvert=false;infoCopiee=false;
nomFichier=null;
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
if(source==ouvrir)
{ String nom = JOptionPane.showInputDialog (this, "nom fichier a
ouvrir");
if((nom==null)||(nom.equals("")))return;
if (fichierOuvert) System.out.println ("On ferme " +
nomFichier);
nomFichier=nom;fichierOuvert=true;
System.out.println("Onouvre"+nomFichier);
}
if(source==fermer)
{ if (fichierOuvert) System.out.println ("On ferme " +
nomFichier);
elseSystem.out.println("pasdefichierouvert");
fichierOuvert=false;
}
if(source==sauvegarder)
{ if (fichierOuvert) System.out.println ("on sauvegarde " +
nomFichier);
else System.out.println ("Pas de fichier ouvert a
sauvegarder");
}
if(source==copier)
{System.out.println("copied'information");
infoCopiee=true;
}
if(source==coller)
{if(infoCopiee)System.out.println("collaged'information");
elseSystem.out.println("Rienacoller");
infoCopiee=false;
}
}
privateJMenuBarbarreMenus;
privateJMenufichier,edition;
336
privateJMenuItemouvrir,sauvegarder,fermer,copier,coller;
privatebooleanfichierOuvert,infoCopiee;
privateStringnomFichier;
}
publicclassFiched2
{publicstaticvoidmain(Stringargs[])
{FenMenufen=newFenMenu();
fen.setVisible(true);
}
}
337
129Activation,désactivationd’options
Modifier le programme réalisé dans l’exercice 121 de manière que ne soientactivées que les seules les options réellement utilisables à unmoment donné.Parexemple,tantqu’unfichiern’estpasouvert,lesoptionsSauvegarderetFermerneserontpasactives.Onproposeradeuxsolutions:
•l’uneoùl’oncontinueradenetraiterquelesévénementsdetypeAction,
•l’autreoùl’ontraiteraenpluslesévénementsdetypeMenuItem.
Nous utiliserons la méthode setEnabled de la classe JMenuItem pour activer oudésactiveruneoption.Initialement(àlaconstructiondelafenêtre),seuleslesoptionsOuvrir et Copier sont activées. Lors du traitement des actions sur les différentesoptions,nousactualisonslesoptionsactives.Plusprécisément,àlafindelaméthodeactionPerformed,nousutilisonslesvaleursdesindicateursbooléensfichierOuvertetinfoCopiee pour décider de l’état des différentes options. Cette démarche est plussimpleque cellequi consisterait àmodifier l’état d’activationd’uneoudeplusieursoptionsenfonctiondel’optionsélectionnée.Notez qu’il est nécessaire de définir l’état d’activation initial des options dans leconstructeur de la fenêtre.Dans le cas contraire, lors de la première sélection d’unmenu,touteslesoptionsseraientactives.Encequi concerne lesmessagesaffichésen fenêtre console, certainsn’ontplus lieud’être,parexemple"pasdefichierouvert"…Nouslesavonssupprimés.
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
importjavax.swing.event.*;
classFenMenuextendsJFrameimplementsActionListener
{publicFenMenu()
338
{setTitle("Exempledemenus");
setSize(300,130);
/*creationbarredesmenus*/
barreMenus=newJMenuBar();
setJMenuBar(barreMenus);
/*creationmenuFichieretsesoptions*/
fichier=newJMenu("Fichier");
barreMenus.add(fichier);
ouvrir=newJMenuItem("Ouvrir");
fichier.add(ouvrir);
ouvrir.addActionListener(this);
sauvegarder=newJMenuItem("Sauvegarder");
fichier.add(sauvegarder);
sauvegarder.addActionListener(this);
fermer=newJMenuItem("Fermer");
fichier.add(fermer);
fermer.addActionListener(this);
/*creationmenuEditionetsesoptions*/
edition=newJMenu("Edition");
barreMenus.add(edition);
copier=newJMenuItem("Copier");
edition.add(copier);
copier.addActionListener(this);
coller=newJMenuItem("Coller");
edition.add(coller);
coller.addActionListener(this);
/*etatinitial:pasdefichierouvert,pasd'infocopiee*/
fichierOuvert=false;infoCopiee=false;
nomFichier=null;
ouvrir.setEnabled(true);
sauvegarder.setEnabled(false);
fermer.setEnabled(false);
copier.setEnabled(true);
coller.setEnabled(false);
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
if(source==ouvrir)
{ String nom = JOptionPane.showInputDialog (this, "nom fichier a
339
ouvrir");
if((nom==null)||(nom.equals("")))return;
if (fichierOuvert) System.out.println ("On ferme " +
nomFichier);
nomFichier=nom;fichierOuvert=true;
System.out.println("Onouvre"+nomFichier);
}
if(source==fermer)
{System.out.println("Onferme"+nomFichier);
fichierOuvert=false;
}
if(source==sauvegarder)
{System.out.println("onsauvegarde"+nomFichier);
}
if(source==copier)
{System.out.println("copied'information");
infoCopiee=true;
}
if(source==coller)
{System.out.println("collaged'information");
infoCopiee=false;
}
/*activation-desactivationdesoptions*/
copier.setEnabled(true);//parsecurite
coller.setEnabled(infoCopiee);
ouvrir.setEnabled(true);//parsecurite
sauvegarder.setEnabled(fichierOuvert);
fermer.setEnabled(fichierOuvert);
}
privateJMenuBarbarreMenus;
privateJMenufichier,edition;
privateJMenuItemouvrir,sauvegarder,fermer,copier,coller;
privatebooleanfichierOuvert,infoCopiee;
privateStringnomFichier;
}
publicclassFiched3a
{publicstaticvoidmain(Stringargs[])
{FenMenufen=newFenMenu();
fen.setVisible(true);
340
}
}
Cette fois, nous tenons compte des événements de typeMenuEvent générés lors del’affichage ou de la disparition d’un menu déroulant. Ceux-ci sont traités par unécouteur implémentant l’interface MenuListener comportant trois méthodesmenuSelected,menuDeselectedetmenuCanceled.Dans lapremière,nousprévoyonsdedéfinirl’étatd’activationdesdifférentesoptions.Iciencore,celui-ciestdéduitdesvaleursdesindicateursbooléensfichierOuvertetinfoCopiee.Parsoucidesimplicité,nousnetestonspaslasource(menuFichieroumenuEdition)etnousdéfinissonsl’étatdetouteslesoptions(alorsquemanifestementnesontconcernéesquecellesdumenuchoisi).Notez que cette fois il n’est plus nécessaire de prévoir une initialisation de l’étatd’activation des options puisque la méthode menuSelected sera nécessairementappeléeavantlepremieraffichaged’unmenu.Touteslesopérationsdegestiondel’étatd’activationsetrouventregroupéesenunseulemplacementduprogramme.
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
importjavax.swing.event.*;
classFenMenuextendsJFrameimplementsActionListener,MenuListener
{publicFenMenu()
{setTitle("Exempledemenus");
setSize(300,130);
/*creationbarredesmenus*/
barreMenus=newJMenuBar();
setJMenuBar(barreMenus);
/*creationmenuFichieretsesoptions*/
fichier=newJMenu("Fichier");
barreMenus.add(fichier);
fichier.addMenuListener(this);
ouvrir=newJMenuItem("Ouvrir");
fichier.add(ouvrir);
ouvrir.addActionListener(this);
sauvegarder=newJMenuItem("Sauvegarder");
fichier.add(sauvegarder);
341
sauvegarder.addActionListener(this);
fermer=newJMenuItem("Fermer");
fichier.add(fermer);
fermer.addActionListener(this);
/*creationmenuEditionetsesoptions*/
edition=newJMenu("Edition");
barreMenus.add(edition);
edition.addMenuListener(this);
copier=newJMenuItem("Copier");
edition.add(copier);
copier.addActionListener(this);
coller=newJMenuItem("Coller");
edition.add(coller);
coller.addActionListener(this);
/*etatinitial:pasdefichierouvert,pasd'infocopiee*/
fichierOuvert=false;infoCopiee=false;
nomFichier=null;
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
if(source==ouvrir)
{ String nom = JOptionPane.showInputDialog (this, "nom fichier a
ouvrir");
if((nom==null)||(nom.equals("")))return;
if (fichierOuvert) System.out.println ("On ferme " +
nomFichier);
nomFichier=nom;fichierOuvert=true;
System.out.println("Onouvre"+nomFichier);
}
if(source==fermer)
{System.out.println("Onferme"+nomFichier);
fichierOuvert=false;
}
if(source==sauvegarder)
{System.out.println("onsauvegarde"+nomFichier);
}
if(source==copier)
{System.out.println("copied'information");
infoCopiee=true;
}
342
if(source==coller)
{System.out.println("collaged'information");
infoCopiee=false;
}
}
publicvoidmenuSelected(MenuEvente)
{/*activation-desactivationdesoptions*/
copier.setEnabled(true);
coller.setEnabled(infoCopiee);
ouvrir.setEnabled(true);
sauvegarder.setEnabled(fichierOuvert);
fermer.setEnabled(fichierOuvert);
}
publicvoidmenuDeselected(MenuEvente){}
publicvoidmenuCanceled(MenuEvente){}
privateJMenuBarbarreMenus;
privateJMenufichier,edition;
privateJMenuItemouvrir,sauvegarder,fermer,copier,coller;
privatebooleanfichierOuvert,infoCopiee;
privateStringnomFichier;
}
publicclassFiched3b
{publicstaticvoidmain(Stringargs[])
{FenMenufen=newFenMenu();
fen.setVisible(true);
}
}
343
130Synthèse:calculssurdesrectangles
Créerunefenêtredisposantd’unebarredemenusdotéededeuxmenusDimensionsetCalculdestinésàeffectuerdescalculsdepérimètreetd’airederectanglesdontonfournitlalongueuretlalargeur.LemenuDimensionscomporteralesoptions:
•Nouvelle longueur qui demandera à l’utilisateur d’entrer dans une boîte desaisieunentierreprésentantunelongueur,
•Nouvellelargeurquidemanderaàl’utilisateurd’entrerdansuneboîtedesaisieunentierreprésentantunelargeur,
• Dimensions actuelles qui affichera dans une boîte de message les valeurscourantesdelalongueuretdelalargeur
Le menu Calculs comportera les options Perimètre et Aire qui afficherontl’informationrequisedansuneboîtedemessageVoici un exemple d’exécution illustrant le fonctionnement de l’option NouvellelongueurdumenuDimensions:
Pour nous faciliter la création des différentes options, nous avons défini (dans lafenêtre)uneméthodeajouterecevantenargumentlaréférenced’unmenu,unlibelléetlaréférencedel’écouteurrequis.Iciencore,ilnoussuffitdetraiterlesévénementsActiondéclenchésparlesdifférentesoptionsdesmenus.Nouslesécoutonsdanslafenêtreelle-même.
344
Uneméthodestatiquelirepermetdelireuneinformationnumériquepositivedansuneboîte de saisie. Nous y traitons le cas d’une réponse non numérique en interceptantl’exception NumberFormatException. Nous signalons à l’utilisateur les réponsesincorrectesparuneboîtedemessageetnousluidemandonsunenouvellevaleur.Nousfaisonsdemêmepourlesvaleursnonpositives.Enrevanche,nouslaissonsàl’utilisateurlapossibilitéde"changerd’avis"enquittantlaboîtedesaisie (par fermetureouparCancel).Nousconvenonsalorsque,danscecas,laméthodelirerenverralavaleur0.
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
importjavax.swing.event.*;
classFenCalculsextendsJFrameimplementsActionListener
{publicFenCalculs()
{setTitle("CALCULSsurdesrectangles");
setSize(400,150);
/*creationbarredesmenus*/
barreMenus=newJMenuBar();
setJMenuBar(barreMenus);
/*creationmenudimensions*/
dimensions=newJMenu("Dimensions");
barreMenus.add(dimensions);
longueur=ajoute(dimensions,"Nouvellelongueur",this);
largeur=ajoute(dimensions,"Nouvellelargeur",this);
infos=ajoute(dimensions,"Dimensionsactuelles",this);
calculs=newJMenu("Calculs");
barreMenus.add(calculs);
perimetre=ajoute(calculs,"Perimetre",this);
aire=ajoute(calculs,"Aire",this);
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
if(source==longueur){intn=lire("Donnezlalongueur");
if(n>0)L=n;
}
if(source==largeur){intn=lire("Donnezlalargeur");
l=n;
}
345
if(source==perimetre)
JOptionPane.showMessageDialog (null, "Perimetre = " + (2*
(L+l)));
if(source==aire)
JOptionPane.showMessageDialog(null,"Aire="+(L*l));
if(source==infos)
JOptionPane.showMessageDialog(null,"Longueur="+L
+"Largeur="+l);
}
privatestaticJMenuItemajoute(JMenumenu,Stringlibelle,
ActionListenerecouteur)
{JMenuItemoption=newJMenuItem(libelle);
menu.add(option);
option.addActionListener(ecouteur);
returnoption;
}
privatestaticintlire(Stringquestion)
{/*iciondemandeunevaleurjusqu'acequ'ellesoitcorrecte*/
/*c'est-a-direentièreetpositive*/
booleancorrect=false;
intvaleur=0;
do
{Stringrep=JOptionPane.showInputDialog(null,question);
if(rep==null)break;//onrenvoie0sifermetureouCancel
try
{valeur=Integer.parseInt(rep);
if(valeur>0)correct=true;
}
catch(NumberFormatExceptionex){}
if (!correct) JOptionPane.showMessageDialog (null, "Valeur
incorrecte");
}
while(!correct);
returnvaleur;
}
privateJMenuBarbarreMenus;
privateJMenudimensions,calculs;
privateJMenuItemlongueur,largeur,perimetre,aire,infos;
privateintl=0,L=0;
}
346
publicclassCalculs
{publicstaticvoidmain(Stringargs[])
{FenCalculsfen=newFenCalculs();
fen.setVisible(true);
}
}
1. Le troisième argument de ajoute a été prévu ici de typeActionListener. Nousappliquonsainsilespossibilitésdepolymorphismeauxinterfaces;ajoutepeutêtreappelée avec un argument d’un type quelconque implémentant l’interfaceActionListener.2.Onpourraitêtretentésd’écrirelaméthodeajoutedecettemanière:
private static void ajoute (JMenu menu, JMenuItem option, String
libelle,
ActionListenerecouteur)
{option=newJMenuItem(libelle);
menu.add(option);
option.addActionListener(ecouteur);
}
etdel’appelerdecettefaçon:ajoute(longueur,dimensions,"Nouvellelongueur",this);
En effet, ajoute recevrait alors dansmenu une copie de la référence figurant danslongueur (ici null), avant de placer dansmenu la référence de l’objet menu crééensuite. Mais la valeur du champ menu de l’objet fenêtre ne serait aucunementmodifiée. Le programme fonctionnerait partiellement mais on ne traiterait pas lesactionssurlesoptions.
347
131Synthèse:colorationparboutonsradio
Créerunefenêtremunied’unebarredemenuscomportantunseulmenu(Couleur)offrantlechoixdelacouleurdelafenêtrepardesboutonsradio:
Pendant l’affichage du menu, la fenêtre deviendra blanche. Si le menu estabandonné,lafenêtrereprendrasacouleurprécédente.
Lescouleurssontconservéesdansuntableaustatiquecouleursd’objetsdetypeColoraccompagné d’un tableau de chaînes nomsCouleurs fournissant le libellécorrespondant. Il est faciled’introduiredenouvelles couleursdans leprogrammeenmodifiantcesdeuxtableaux.LacréationdumenuCouleurneposeaucunproblème.Celledesesoptionssefaitparunebouclesur lesdifférentescouleurs ; lenombrederépétitionsestsimplementfixéparladimensiondutableaucouleurs.Ici,ilestnécessairedetraiteràlafoislesévénementsActionetMenugénérésparlesoptions dumenuCouleur. Les premiers fixent la couleur de fond de la fenêtre, lessecondspermettentdedéciderdumomentoùlafenêtredoitêtrerepeinteenblanc.Encequiconcernelechangementdecouleurdelafenêtre,onnepeutpassecontenterd’appeler sa méthode setBackground aux moments opportuns (actionPerformed,menuSelected…).Eneffet,lesmodificationsd’affichagedumenului-mêmenécessitentque la fenêtre soit repeinte.Nous pourrions redéfinir laméthodepaint de la fenêtre
348
elle-même mais, par souci de généralité, nous préférons utiliser la méthodepaintComponentd’unpanneauoccupanttoutelafenêtre.NouscréonsdoncuneclassePanneau, dérivée de JPanel. La couleur courante est définie par une variablecouleurCourantefigurantdanslafenêtreetàlaquellelepanneaupeutaccéderparuneméthodegetCouleur (il a fallu fournir au constructeur dupanneau la référencede lafenêtreconcernée).Letraitementd’uneactionsuruneoptionconsiteàtrouverlacouleurcorrespondanteenexplorant le tableau d’options. En fait, nous définissons à la fois une couleur et unnuméro,cederniernouspermettantderetrouverl’anciennecouleurcourante(oucellequi vient d’être sélectionnée) lorsque se produit l’événement correspondant àmenuDeselected.Onnoteraquecedernier seproduit après l’événementAction (s’ilexiste). Si l’on tient à être indépendant de cet ordre, on peut toujours définir la(nouvelle)couleurcouranteàlafoisdansmenuDeselectedetdansactionPerformedetenappelerrepaintdansactionPerformed (ce qui n’est pas nécessaire puisqu’il seraappeléaprèsmenuDeselectedsuiteàl’effacementdumenu).
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
importjavax.swing.event.*;
classFenMenuextendsJFrameimplementsActionListener,MenuListener
{staticColor[]couleurs=
{Color.red,Color.yellow,Color.blue,Color.green};
staticString[]nomsCouleurs=
{"Rouge","Jaune","Bleu","Vert"};
publicFenMenu()
{setTitle("COULEURS");setSize(300,150);
/*creationpanneauoccupanttoutelafenetre*/
panneau=newPanneau(this);
getContentPane().add(panneau);
/*creationbarredesmenus*/
barreMenus=newJMenuBar();
setJMenuBar(barreMenus);
/*creationmenuCouleuretsesoptions*/
menuCouleur=newJMenu("Couleur");
barreMenus.add(menuCouleur);
menuCouleur.addMenuListener(this);
nbCouleurs=couleurs.length;
optionsCouleurs=newJRadioButtonMenuItem[nbCouleurs];
ButtonGroupgroupe=newButtonGroup();
349
for(inti=0;i<nbCouleurs;i++)
{ optionsCouleurs[i] = new JRadioButtonMenuItem
(nomsCouleurs[i]);
menuCouleur.add(optionsCouleurs[i]);
optionsCouleurs[i].addActionListener(this);
groupe.add(optionsCouleurs[i]);
}
couleurCourante=couleurs[numCouleur];
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
for(inti=0;i<nbCouleurs;i++)
if(source==optionsCouleurs[i])
{numCouleur=i;
couleurCourante=couleurs[numCouleur];
repaint() ; // pour forcer a repeindre l’ensemble de la
fenetre
}
}
publicvoidmenuSelected(MenuEvente)
{couleurCourante=Color.white;
}
publicvoidmenuDeselected(MenuEvente)
{couleurCourante=couleurs[numCouleur];
}
publicvoidmenuCanceled(MenuEvente){}
publicColorgetCouleur()
{returncouleurCourante;
}
privatePanneaupanneau;
privateJMenuBarbarreMenus;
privateJMenumenuCouleur;
privateJRadioButtonMenuItemoptionsCouleurs[];
privateintnbCouleurs;
privateintnumCouleur=0;
privateColorcouleurCourante;
}
classPanneauextendsJPanel
{publicPanneau(FenMenufen)
{this.fen=fen;
350
}
publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
setBackground(fen.getCouleur());
}
privateFenMenufen;
}
publicclassCoul1
{publicstaticvoidmain(Stringargs[])
{FenMenufen=newFenMenu();
fen.setVisible(true);
}
}
Si on n’appelle pas la méthode repaint dans actionPerformed, la fenêtre risque den’êtrequepartiellementrepeinte.Eneffet,lorsdelafermeturedumenu,JavaappellebienpaintComponent pour repeindre la fenêtre,mais en se limitant à la seulepartieendommagée1(nomméesouvent"rectangleinvalide").
351
132Synthèse:choixdecouleurdefondetdeformepardesmenuscomposés
Afficher un rectangle coloré de taille fixe dans une fenêtre. Un menu Couleur,constituédedeuxsous-menusFondetFormepermettradechoisirlacouleurdufondoudurectangledansunelistedecouleurs(quiseralamêmepourlesdeuxcas):
Lescouleurset leursnomsseront fournis sous formede tableauxenargumentsduconstructeurdelafenêtre.Le dessin d’un rectangle de couleur donnée se fait en appliquant au contextegraphique concerné successivement la méthode setColor (en argument l’objet detypeColorvoulu)etlaméthodefillRect(intabscisse,intordonnee,intlargeur,inthauteur)).
Nous dessinons dans un panneau dont nous redéfinissons classiquement la méthodepaintComponent.Celanécessitelacréationd’uneclassespécialiséePanneaudérivéedeJPanel.Ici,nousavonsaffaireàdesmenuscomposés:lemenuCouleurcomportedeuxsous-menusFormeetFond(quisonttoujoursdesobjetsdetypeJMenu).Acesderniers,on
352
rattachedesoptionsdetypeJMenuItem.Ilnoussuffitd’écouterlesévénementsActionqu’ilsgénérent.LesvariablescouleurFondetcouleurFormeserventàmémoriserdanslafenêtreladernièrecouleursélectionnée.LaméthodepaintComponentdupanneauyaccèdeàl’aidedesméthodesgetCouleurFondetgetCouleurForme.
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
importjavax.swing.event.*;
classFenRectextendsJFrameimplementsActionListener
{publicFenRect(Color[]couleurs,String[]nomsCouleurs)
{setTitle("Couleursdefondetdeforme");
setSize(350,220);
this.couleurs=couleurs;
this.nomsCouleurs=nomsCouleurs;
/*creationbarredesmenus*/
barreMenus=newJMenuBar();
setJMenuBar(barreMenus);
/*creationmenuCouleuretsous-menusFondetForme*/
couleur=newJMenu("Couleur");
barreMenus.add(couleur);
menuCouleurFond=newJMenu("Fond");
couleur.add(menuCouleurFond);
menuCouleurForme=newJMenu("Forme");
couleur.add(menuCouleurForme);
/*creationdesoptionsdecouleuretajoutauxdeuxsous-menus
*/
nbCouleurs=couleurs.length;
optionsCouleurFond=newJMenuItem[nbCouleurs];
optionsCouleurForme=newJMenuItem[nbCouleurs];
for(inti=0;i<nbCouleurs;i++)
{optionsCouleurForme[i]=newJMenuItem(nomsCouleurs[i]);
optionsCouleurForme[i].addActionListener(this);
menuCouleurForme.add(optionsCouleurForme[i]);
optionsCouleurFond[i]=newJMenuItem(nomsCouleurs[i]);
optionsCouleurFond[i].addActionListener(this);
menuCouleurFond.add(optionsCouleurFond[i]);
}
/*creationpanneaudedessin*/
353
panneau=newPanneau(this);
getContentPane().add(panneau);
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
for(inti=0;i<nbCouleurs;i++)
{ if (source == optionsCouleurFond[i]) couleurFond =
couleurs[i];
if (source == optionsCouleurForme[i]) couleurForme =
couleurs[i];
}
panneau.repaint();//pourforcerarepeindrel'ensembledela
fenetre
}
publicColorgetCouleurFond(){returncouleurFond;}
publicColorgetCouleurForme(){returncouleurForme;}
privateColor[]couleurs;
privateString[]nomsCouleurs;
privateJMenuBarbarreMenus;
privatePanneaupanneau;
privateJMenucouleur,menuCouleurFond,menuCouleurForme;
privateJMenuItem[]optionsCouleurFond,optionsCouleurForme;
privateintnbCouleurs;
privateColorcouleurFond=Color.white,couleurForme=Color.black;
}
classPanneauextendsJPanel
{privatestaticintx=10,y=10,largeur=200,hauteur=120;
publicPanneau(FenRectfen)
{this.fen=fen;
}
publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
setBackground(fen.getCouleurFond());
g.setColor(fen.getCouleurForme());
g.fillRect(x,y,largeur,hauteur);
}
privateFenRectfen;
}
publicclassComposes
354
{privatestaticColor[]couleurs=
{Color.yellow, Color.red, Color.blue, Color.pink, Color.green
};
privatestaticString[]nomsCouleurs=
{"jaune","rouge","bleu","rose","vert"};
publicstaticvoidmain(Stringargs[])
{FenRectfen=newFenRect(couleurs,nomsCouleurs);
fen.setVisible(true);
}
}
355
133Synthèse:choixdecouleursetdedimensionspardesmenussurgissants
Afficher un rectangle coloré dans une fenêtre. Un clic dans le rectangle feraapparaîtreunmenusurgissantpermettantdemodifierlesdimensionsdurectangleousa couleur. Un clic en dehors du rectangle fera apparaître un menu surgissantpermettantdemodifierlacouleurdufond.
Lescouleursetleursnomsserontlesmêmespourlefondetpourlerectangleetilsserontfournissousformedetableauxenargumentsduconstructeurdelafenêtre.Le dessin d’un rectangle de couleur donnée se fait en appliquant au contextegraphique concerné successivement la méthode setColor (en argument l’objet detypeColorvoulu)etlaméthodefillRect(intabscisse,intordonnee,intlargeur,inthauteur)).
Note:larésolutiondecetexerciceserafacilitéeparcelledel’exercice132.
Nous dessinons dans un panneau dont nous redéfinissons classiquement la méthode
356
paintComponent.Celanécessitelacréationd’uneclassespécialiséePanneaudérivéedeJPanel.Dansleconstructeurdelafenêtre,nouscréonsdeuxmenussurgissantsmenuFormeetmenuFond. Le premier est constitué de deux sous-menus (de type JMenu)menuFormeDimensions et menuFormeCouleurs. Leurs options sont de typeJMenuItem.Nousavonschoisid’écouter lesdifférentesoptionsdans la fenêtreelle-même (les écouter dans le panneau aurait nécessité de lui fournir les références detouteslesoptionsconcernées).LesvariablescouleurFond,couleurForme,lethserventàmémoriserdanslafenêtreles dernières couleurs sélectionnées et les dimensions du rectangle. La méthodepaintComponent du panneau y accède à l’aide des méthodes getCouleurFond,getCouleurForme,getLargeuretgetHauteur.Lesdimensionssontluesdansdesboîtesdesaisie.Ontraitecommeàl’accoutuméelesexceptionsdeconversion.Ledéclenchementdesmenussurgissantsalieuencasdeclicdanslepanneaudontnousfaisonssonpropreécouteurd’événementsMouse. Ici, l’affichagedumenuestréalisélors du relâchement du bouton attribué aux menus surgissants : nous redéfinissonsmouseReleased et nous testons le bouton concerné à l’aide de la méthodeisPopupTriggerdelaclasseMouseEvent.L’objetpanneaun’abesoindeconnaîtrequela référence de la fenêtre et celles des deux menus surgissants. Celles-ci sonttransmisesauconstructeur.
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
importjavax.swing.event.*;
classFenRectextendsJFrameimplementsActionListener
{publicFenRect(Color[]couleurs,String[]nomsCouleurs)
{setTitle("Menussurgissantscomposes");
setSize(300,150);
this.couleurs=couleurs;
this.nomsCouleurs=nomsCouleurs;
/*creationmenussurgissantsFondetForme*/
menuFond=newJPopupMenu();
menuForme=newJPopupMenu();
menuFormeCouleur=newJMenu("Couleur");
menuForme.add(menuFormeCouleur);
menuFormeDimensions=newJMenu("Dimensions");
357
menuForme.add(menuFormeDimensions);
/*creationdesoptions*/
nbCouleurs=couleurs.length;
optionsCouleurFond=newJMenuItem[nbCouleurs];
optionsCouleurForme=newJMenuItem[nbCouleurs];
for(inti=0;i<nbCouleurs;i++)
{optionsCouleurForme[i]=newJMenuItem(nomsCouleurs[i]);
optionsCouleurForme[i].addActionListener(this);
menuFormeCouleur.add(optionsCouleurForme[i]);
optionsCouleurFond[i]=newJMenuItem(nomsCouleurs[i]);
optionsCouleurFond[i].addActionListener(this);
menuFond.add(optionsCouleurFond[i]);
}
optionHauteur=newJMenuItem("Hauteur");
optionLargeur=newJMenuItem("Largeur");
menuFormeDimensions.add(optionHauteur);
menuFormeDimensions.add(optionLargeur);
optionHauteur.addActionListener(this);
optionLargeur.addActionListener(this);
/*creationpanneaudedessin*/
panneau=newPanneau(this,menuForme,menuFond);
panneau.addMouseListener(panneau);
getContentPane().add(panneau);
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
for(inti=0;i<nbCouleurs;i++)
{ if (source == optionsCouleurFond[i]) couleurFond =
couleurs[i];
if (source == optionsCouleurForme[i]) couleurForme =
couleurs[i];
}
if((source==optionLargeur)||(source==optionHauteur))
{intvaleur=0;Stringquestion;
booleanok=false;
if(source==optionLargeur)question="Nouvellelargeur?";
elsequestion="Nouvellehauteur?";
Stringrep=JOptionPane.showInputDialog(null,question);
try
{valeur=Integer.parseInt(rep);
358
ok=true;
}
catch(NumberFormatExceptionex){}
if(ok)if(source==optionLargeur)l=valeur;
elseh=valeur;
}
panneau.repaint();//pourforcerarepeindrel'ensembledela
fenetre
}
publicColorgetCouleurFond(){returncouleurFond;}
publicColorgetCouleurForme(){returncouleurForme;}
publicintgetLargeur(){returnl;}
publicintgetHauteur(){returnh;}
privateColor[]couleurs;
privateString[]nomsCouleurs;
privatePanneaupanneau;
privateJPopupMenumenuFond,menuForme;
privateJMenumenuFormeCouleur,menuFormeDimensions;
privateJMenuItem[]optionsCouleurFond,optionsCouleurForme;
privateJMenuItemoptionHauteur,optionLargeur;
privateintnbCouleurs;
privateColorcouleurFond=Color.white,couleurForme=Color.black;
privateintl=100,h=50;
}
classPanneauextendsJPanelimplementsMouseListener
{privatestaticintx=10,y=10;
public Panneau (FenRect fen, JPopupMenu menuForme, JPopupMenu
menuFond)
{this.fen=fen;
this.menuForme=menuForme;
this.menuFond=menuFond;
}
publicvoidmouseReleased(MouseEvente)
{if(!e.isPopupTrigger())return;
intxClic=e.getX(),yClic=e.getY();
if ((xClic>=x) && (xClic<=x+largeur) && (yClic>=y) &&
(yClic<=y+hauteur))
menuForme.show(fen,xClic,yClic);
else
359
menuFond.show(fen,xClic,yClic);
}
publicvoidmousePressed(MouseEvente){}
publicvoidmouseClicked(MouseEvente){}
publicvoidmouseEntered(MouseEvente){}
publicvoidmouseExited(MouseEvente){}
publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
setBackground(fen.getCouleurFond());
g.setColor(fen.getCouleurForme());
largeur=fen.getLargeur();
hauteur=fen.getHauteur();
g.fillRect(x,y,largeur,hauteur);
}
privateFenRectfen;
privateintlargeur,hauteur;
privateJPopupMenumenuForme,menuFond;
}
publicclassCompsurg
{privatestaticColor[]couleurs=
{Color.yellow, Color.red, Color.blue, Color.pink, Color.green
};
privatestaticString[]nomsCouleurs=
{"jaune","rouge","bleu","rose","vert"};
publicstaticvoidmain(Stringargs[])
{FenRectfen=newFenRect(couleurs,nomsCouleurs);
fen.setVisible(true);
}
}
1.Plusprécisémentaupluspetitrectanglecontenantlapartieendommagée.
360
Chapitre13
Lesévénementsdebasniveau
Connaissancesrequises
• Événements de type MouseEvent liés aux boutons de la souris (rappel) ;méthodesmousePressed,mouseReleasedetmouseClicked• Identification du bouton de la souris ; méthodes getModifiers et constantescorrespondantesInputEvent.BUTTON1_MASK, InputEvent.BUTTON2_MASKetInputEvent.BUTTON3_MASK•Gestiondesclicsmultiples;méthodegetClickCount
•Gestiondesdéplacementsdelasouris;méthodesmouseEntered,mouseExited,mouseMovedetmouseDragged
•ÉvénementsdetypeKeyEvent;méthodeskeyPressed,keyReleasedetkeyTyped;identificationd’unetoucheparsoncodedetouchevirtuelle(méthodegetKeyCode)ouparlecaractèrecorrespondant(méthodegetKeyChar);connaissancedel’étatdestouchesmodificatrices(méthodesisXXXDownetgetModifiers);sourced’unévénementclavier
361
134Identificationdesboutonsdelasouris
Afficherenpermanenceunsegmentdansunefenêtre.Sonorigineseradéfinieparunclicsurleboutondegauchedelasourisetellesemodifieraàchaquenouveauclicsurcemêmebouton.Sonextrémitéseradéfiniedelamêmemanièreavecleboutondedroite:
Pourobtenir lapermanencedudessin,noustraceronsnotresegmentdansunpanneau.Ici, il est plus simpled’écouter les clics (mouseClicked) dans le panneau lui-même.Pour identifier le bouton de la souris, nous utilisons laméthode getModifiers de laclasseMouseEvent.Ellefournitunentierdanslequelunbitderangdonné,associéàchacun des boutons, prend la valeur 1.La classe InputEvent contient des constantesqu’onpeututilisercommemasquepourfaciliterleschoses;ici,cesontlesconstantesBUTTON1_MASK(boutondegauche)etBUTTON3_MASK(boutondedroite)quinousintéressent.Lesegmentestdéfiniparlescoordonnéesdesonorigine(xOretyOr)etcellesdesonextrémité(xExtetyExt).DeuxindicateursbooléensorConnueetextConnuepermettentde savoir si ces informations sont disponibles (elles sont en fait placées à false audébut du programme). Le dessin proprement dit est réalisé dans la méthodepaintComponent qui exploite cesdifférentes informations.Notezqu’il estnécessaired’appeler repaint après un clic gauche ou droite, afin de provoquer l’appel depaintComponent.
362
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrame
{publicMaFenetre()
{setTitle("SEGMENT");
setSize(300,150);
pan=newPanneau();
getContentPane().add(pan);
pan.addMouseListener(pan);
}
privatePanneaupan;
}
classPanneauextendsJPanelimplementsMouseListener
{publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
if(orConnue&&extConnue)g.drawLine(xOr,yOr,xExt,yExt);
}
publicvoidmousePressed(MouseEvente)
{intx=e.getX(),y=e.getY();
intmodifieurs=e.getModifiers();
if((modifieurs&InputEvent.BUTTON1_MASK)!=0)
{/*clicboutongauche*/
xOr=x;yOr=y;
orConnue=true;
repaint();
}
if((modifieurs&InputEvent.BUTTON3_MASK)!=0)
{/*clicboutondroite*/
xExt=x;yExt=y;
extConnue=true;
repaint();
}
}
publicvoidmouseReleased(MouseEvente){}
publicvoidmouseClicked(MouseEvente){}
publicvoidmouseEntered(MouseEvente){}
publicvoidmouseExited(MouseEvente){}
privateintxOr,yOr,xExt,yExt;
363
privatebooleanorConnue=false,extConnue=false;
}
publicclassSegments
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
364
135Vraisdoubles-clics
Javanedisposequed’unseulcompteurdeclicspour lesdifférentsboutonsde lasouris.Danscesconditions,iln’estpaspossiblededistinguerunvéritabledouble-clicdedeuxclicssuccessifssurdeuxboutonsdifférents.Écrireunprogrammequidétecte les "vrais" doubles-clics sur le bouton de gauche et qui affiche alors unmessageenfenêtreconsole.
Nousferonsnaturellementdelafenêtresonpropreécouteurd’événementssouris.Nousutiliserons:
•laméthodegetClickCountquifournitlenombredeclics(rapprochés)successifs,
•laméthodegetModifierspouridentifierleboutondelasouris.Un indicateur booléen clicGauche indique si le dernier clic concernait le bouton degauche.Ilfautbienprendregardeà:
•mettrel’indicateurclicGaucheàfalseaprèsundouble-clicgauche(vraioufaux)ainsiqu’aprèstoutclicsurunautrebouton,
•mettrel’indicateurclicGaucheàtrueaprèsunsimpleclicgauche.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrameimplementsMouseListener
{publicMaFenetre()
{setTitle("DOUBLESCLICS");
setSize(300,150);
clicGauche=false;
addMouseListener(this);
}
publicvoidmousePressed(MouseEvente){}
365
publicvoidmouseReleased(MouseEvente){}
publicvoidmouseClicked(MouseEvente)
{intmodifieurs=e.getModifiers();
if((modifieurs&InputEvent.BUTTON1_MASK)!=0)
/*ici,onaaffaireaunclicgauche*/
{if((e.getClickCount()==2)&&clicGauche)
{System.out.println("Doubleclicgauche");
clicGauche=false;
}
elseclicGauche=true;
}
elseclicGauche=false;
}
publicvoidmouseEntered(MouseEvente){}
publicvoidmouseExited(MouseEvente){}
privatebooleanclicGauche;
}
publicclassDoubClic
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
366
136Suividesdéplacementsdelasouris(1)
Créerune fenêtredotéed’unbouton.Afficheren fenêtreconsoledesmessagesdesuividesdéplacementsdelasouriscommedanscetexemple:
lasourisentredanslafenetre
lasourisquittelafenetre
lasourisentredanslebouton
lasourisquittelebouton
lasourisentredanslafenetre
lasourisquittelafenetre
lasourisentredanslafenetre
lasourisquittelafenetre
lasourisentredanslebouton
lasourisquittelebouton
lasourisentredanslafenetre
lasourisquittelafenetre
IlnoussuffitdesuivrelesévénementsmouseEnteredetmouseExitedayantpoursourceleboutonoulafenêtre.Ici,nousutilisonspourlesdeuxunmêmeécouteur,àsavoirlafenêtreelle-même.Nousy redéfinissons les sixméthodesprévuespar l’interfaceMouseListener ; ici ce sontMouseEntered et MouseExited qui nous intéressent. Dans chacune de ces deuxméthodes,getSourcenouspermetd’identifierlasourcedel’événement.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrameimplementsMouseListener
{publicMaFenetre()
{setTitle("Evenementssouris");
setSize(300,150);
367
contenu=getContentPane();
contenu.setLayout(newFlowLayout());
addMouseListener(this);
bouton=newJButton("A");
contenu.add(bouton);
bouton.addMouseListener(this);
}
publicvoidmousePressed(MouseEvente){}
publicvoidmouseReleased(MouseEvente){}
publicvoidmouseClicked(MouseEvente){}
publicvoidmouseEntered(MouseEvente)
{if(e.getSource()==this)
System.out.println("lasourisentredanslafenetre");
if(e.getSource()==bouton)
System.out.println("lasourisentredanslebouton");
}
publicvoidmouseExited(MouseEvente)
{if(e.getSource()==this)
System.out.println("lasourisquittelafenetre");
if(e.getSource()==bouton)
System.out.println("lasourisquittelebouton");
}
privateJButtonbouton;
privateContainercontenu;
}
publicclassDpSour
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
L’exemple d’exécution de l’énoncé montre bien que lorsque la souris entre dans lebouton,ellesortdelafenêtre.
368
Voiciuneautresolutiondanslaquellelafenêtreet leboutonontétéchacundotéd’unécouteurobjetd’uneclasseanonyme(implémentantl’interfaceMouseAdapter). Ici, iln’estplusnécessairedetesterlasourced’unévénement.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrame
{publicMaFenetre()
{setTitle("Evenementssouris");
setSize(300,150);
contenu=getContentPane();
contenu.setLayout(newFlowLayout());
addMouseListener(newMouseAdapter()
{publicvoidmouseEntered(MouseEvente)
{System.out.println("lasourisentredanslafenetre");
}
publicvoidmouseExited(MouseEvente)
{System.out.println("lasourisquittelafenetre");
}
});
bouton=newJButton("A");
contenu.add(bouton);
bouton.addMouseListener(newMouseAdapter()
{publicvoidmouseEntered(MouseEvente)
{System.out.println("lasourisentredanslebouton");
}
publicvoidmouseExited(MouseEvente)
{System.out.println("lasourisquittelebouton");
}
});
}
privateJButtonbouton;
privateContainercontenu;
}
publicclassDpSourb
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
369
}
}
Voici une troisième solution dans laquelle la fenêtre et le bouton partagent lemêmeécouteur, là encore objet d’une classe anonyme implémentant l’interfaceMouseAdapter.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrame
{publicMaFenetre()
{setTitle("Evenementssouris");
setSize(300,150);
contenu=getContentPane();
contenu.setLayout(newFlowLayout());
MouseAdapterecout=newMouseAdapter()
{publicvoidmouseEntered(MouseEvente)
{if(e.getSource()==contenu)
System.out.println("lasourisentredanslafenetre");
if(e.getSource()==bouton)
System.out.println("lasourisentredanslebouton");
}
publicvoidmouseExited(MouseEvente)
{if(e.getSource()==contenu)
System.out.println("lasourisquittelafenetre");
if(e.getSource()==bouton)
System.out.println("lasourisquittelebouton");
}
};
contenu.addMouseListener(ecout);
bouton=newJButton("A");
contenu.add(bouton);
bouton.addMouseListener(ecout);
}
privateJButtonbouton;
privateContainercontenu;
370
}
publicclassDpSoura
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
Ici, par souci de simplicité, nous avons intercepté les événements ayant pour sourcenonplus lafenêtreelle-même,maissoncontenu.Eneffet,dans laclasseanonymedel’écouteur, on ne peut plus identifier la fenêtre par this. On pourrait le faire enconservantlaréférencedelafenêtredansunchamp.
371
137Suividesdéplacementsdelasouris(2)
Réaliser une fenêtre disposant d’un bouton marqué CREATION_BOUTONSpermettantdecréerdynamiquementdesboutonsmarquésB1,B2,B3…
Lorsque la souris"passe"sur l’undecesboutons, il secoloreen fonctiondesonnuméro(parexemple,lepremierenrouge,lesecondenjaune,letroisièmeenvert,lequatrièmeenbleu,lecinquièmeànouveauenrouge…);leboutonsecoloreenblanclorsquelasourisensort
Ici,nousfaisonsdelafenêtrel’uniqueécouteurdesdifférentesévénements:
•Actionpourleboutondecréation,
•Mousepourlesboutonsdynamiques.Nousnouscontentonsdugestionnairepardéfautdelafenêtre.Pouridentifierleboutonconcernéparunévénementsouris,nousaurionspuutiliserlaréférence à la source fournie par getSource. Cela aurait toutefois nécessité deconserver les références de tous les boutons créés dynamiquement. Ici, nous avonschoisid’exploiter lachaînedecommandede la source ;elle s’obtientà l’aidede laméthode getActionCommand qui figure dans toutes les classes dérivées deAbstractButton,doncenparticulierdansJButton1.
importjavax.swing.*;
372
importjava.awt.*;
importjava.awt.event.*;
class MaFenetre extends JFrame implements MouseListener,
ActionListener
{ static final Color couleurs[] = {Color.red, Color.yellow,
Color.green,
Color.blue};
publicMaFenetre()
{setTitle("Evenementssouris");setSize(300,150);
contenu=getContentPane();
contenu.setLayout(newFlowLayout());
boutonCreation=newJButton("CREATION_BOUTONS");
contenu.add(boutonCreation);
boutonCreation.addActionListener(this);
}
publicvoidactionPerformed(ActionEvente)
{if(e.getSource()==boutonCreation)
{numBouton++;
JButtonb=newJButton("B"+numBouton);
contenu.add(b);
b.addMouseListener(this);
}
}
publicvoidmousePressed(MouseEvente){}
publicvoidmouseReleased(MouseEvente){}
publicvoidmouseClicked(MouseEvente){}
publicvoidmouseEntered(MouseEvente)
{Objectsource=e.getSource();
JButtonbSource;
if(sourceinstanceofJButton)//parprecaution
{bSource=(JButton)source;
Stringch=bSource.getActionCommand();
if(ch.charAt(0)=='B')
{intn=Integer.parseInt(ch.substring(1));
intnumCoul=n%couleurs.length;
bSource.setBackground(couleurs[numCoul]);
}
}
}
publicvoidmouseExited(MouseEvente)
373
{Objectsource=e.getSource();
JButtonbSource;
if(sourceinstanceofJButton)//parprecaution
{bSource=(JButton)source;
Stringch=bSource.getActionCommand();
if(ch.charAt(0)=='B')
bSource.setBackground(Color.white);
}
}
privateContainercontenu;
privateJButtonboutonCreation;
privateintnumBouton=0;
}
publicclassBtDynCol
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
Dans lesméthodesmouseEntered etmouseExited, nous nous sommes assurés que lasource était bien d’un type JButton (opérateur instanceof) avant de lui appliquer laméthodegetActionCommand. Ce n’était pas indispensable ici,mais cela pourrait ledevenirsil’onmodifiaitleprogrammeenécoutantlesévénementssourisgénéréspard’autrescomposants.
374
138Dessinparleclavier(1)
Écrireunprogrammepermettantdedessineràlavoléedansunefenêtreenutilisantlestouchesfléchéesduclavier:
Ledessincommenceraenunpointdonnédelafenêtre(ici20×20).Onpourrafixerunincrémentdeplusieurspixels(ici5)pourchaqueappuisurunetouche.
Comme il s’agit ici de dessin à la volée, nous aurions pu opérer directement sur lafenêtre (ouplutôt sur soncontenu).Mais,pourconserverauprogrammeuncaractèreplusgénéral,nousavonspréférédessinersurunpanneau.Lapositiondedébutdudessinestfixéeparlesvaleursinitialesdesvariablesxetyquidésignentensuitelapositioncourantedefindedessin.L’incrémentdudéplacementestfixéparlesconstantesincxetincy.Nouspouvonsfairede lafenêtre l’écouteurdesévénementsclavier.Eneffet,ceux-ciseronttransmisàlafoisaupanneauetàsonconteneur,c’est-à-direlafenêtre.Ici,nousredéfinissonslaméthodekeyPressed,cequirevientàdirequenousdécidonsquelesdéplacementsseronteffectuéslorsdel’appuidestouches.LaméthodegetKeyCodedela classeKeyEvent nous permet de connaître le code de touche virtuelle concerné.NousutilisonslesconstantestellesqueKeyEvent.KEY_UPpouridentifierlestouchesfléchées.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
375
classMaFenetreextendsJFrameimplementsKeyListener
{staticintincx=5,incy=5;
publicMaFenetre()
{setTitle("DESSINAUCLAVIER");setSize(350,150);
addKeyListener(this);
pan=newJPanel();
getContentPane().add(pan);
}
publicvoidkeyPressed(KeyEvente)
{intcode=e.getKeyCode();
switch(code)
{ case KeyEvent.VK_UP : dx = 0 ; dy = -incy ; bouge = true ;
break;
case KeyEvent.VK_DOWN : dx = 0 ; dy = incy ; bouge = true ;
break;
case KeyEvent.VK_LEFT : dx = -incx ; dy = 0 ; bouge = true ;
break;
case KeyEvent.VK_RIGHT : dx = incx ; dy = 0 ; bouge = true ;
break;
}
if(bouge)
{Graphicsg=pan.getGraphics();
g.drawLine(x,y,x+dx,y+dy);
g.dispose();
x+=dx;y+=dy;
}
}
publicvoidkeyReleased(KeyEvente){}
publicvoidkeyTyped(KeyEvente){}
privateJPanelpan;
privateintx=20,y=20;
privateintdx,dy;
privatebooleanbouge;
}
publicclassDesClav
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
376
139Synthèse:dessinparleclavier(2)
Modifier le programme de l’exercice, de manière qu’on puisse interrompre ledessinetlereprendreenunautrepoint:
Unmotif(enformedex)permettradevisualiserlapositioncourantedu"curseur".Les touches fléchées agiront toujours sur la position du curseur ; en revanche, ledessinn’auralieuquesilatoucheShiftestenfoncée.
Note : cet exercice nécessite (en plus des prérequis mentionnés en début de cechapitre et du précédent) de savoir ce qu’est un "mode de dessin" et comment lemodifier.
Les touches fléchéesprovoqueront donc toujours le déplacementdu curseur.Pour cefaire,ilestnécessairedepouvoireffacerlecurseurdesonanciennepositionetdeletracerdanssanouvelleposition.Pouryparvenir, leplussimpleconsisteàutiliserlemodededessinditXOR,enleparamétrantparlacouleurdefonddupanneau.Danscecas,eneffet:
•ledessinsurunezoneayantlacouleurdefondestfaitaveclacouleurcourante,
•lemêmedessineffectuédeuxfoisdesuiteeffacelepremier.Encequiconcernel’éventueltracédutrait,ilfautcettefoistenircomptedel’étatdelatouche Shift. On l’obtient avec la méthode getModifiers qui fournit un entier danslequelunbitderangInputEvent.SHIFT_MASKcorrespondàlatoucheShift.Onnoteraquesil’ontraçaitcetraitdanslemodeXOR,oneffacerait lepointsituéà
377
l’intersection des deux segments représentant le curseur. On pourrait éventuellementprévoird’afficherànouveaucepointmaiscettedémarcheseraitdépendantedumotifutilisépourlecurseur.Leplusraisonnableconsisteàafficherletraitdanslemodededessinnormalqu’onobtientparappeldesetPaintMode.Initialement,aucuncurseurnes’affichedanslafenêtre.Eneffet,nousnepouvonspaseffectuer ce tracé dans le constructeur de la fenêtre car aucun contexte graphique neseraitencoredisponiblepourlepanneau(laméthodegetGraphicsfourniraitlavaleurnull).Parsoucidesimplicité,nousnoussommesdonccontentésd’affichercecurseuraprèslapremièreactionsurunetouchefléchée(nousrecouronsàunindicateurbooléennommédebut).
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrameimplementsKeyListener
{staticintincx=5,incy=5;
publicMaFenetre()
{setTitle("DESSINAUCLAVIER");
setSize(350,150);
addKeyListener(this);
pan=newJPanel();
getContentPane().add(pan);
}
publicvoidkeyPressed(KeyEvente)
{intcode=e.getKeyCode();
bouge=false;
switch(code)
{ case KeyEvent.VK_UP : dx = 0 ; dy = -incy ; bouge = true ;
break;
case KeyEvent.VK_DOWN : dx = 0 ; dy = incy ; bouge = true ;
break;
case KeyEvent.VK_LEFT : dx = -incx ; dy = 0 ; bouge = true ;
break;
case KeyEvent.VK_RIGHT : dx = incx ; dy = 0 ; bouge = true ;
break;
}
if(bouge)
{Graphicsg=pan.getGraphics();
g.setXORMode(pan.getBackground());
/*effacel'anciencurseur(s'ilexiste)etaffichelenouveau
*/
378
if(debut)debut=false;
elseafficheCurseur(g,x,y);
afficheCurseur(g,x+dx,y+dy);
g.setPaintMode();
/*onnetracequesilatoucheShiftestenfoncee*/
if((e.getModifiers()&InputEvent.SHIFT_MASK)!=0)
g.drawLine(x,y,x+dx,y+dy);
x+=dx;
y+=dy;
g.dispose();
}
}
privatevoidafficheCurseur(Graphicsg,intx,inty)
{intdx=2,dy=2;
g.drawLine(x-dx,y-dy,x+dx,y+dy);
g.drawLine(x-dx,y+dy,x+dx,y-dy);
}
publicvoidkeyReleased(KeyEvente){}
publicvoidkeyTyped(KeyEvente){}
privateJPanelpan;
privateintx=20,y=20;
privateintdx,dy;
privatebooleanbouge;
privatebooleandebut=true;
}
publicclassDesClav2
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
379
140Sélectiond’uncomposantparleclavier
Afficherdansunefenêtrenboutons(n<=9)étiquettésde1àn.Faireensortequelafrappedel’unedestouches1ànsélectionneleboutondenuméron (luidonne lefocus).
Nous introduisons classiquement les boutons dans la fenêtre, en conservant leursréférences dans un tableauboutons. Nous faisons de la fenêtre son propre écouteurd’événements clavier et nous redéfinissons lesméthodeskeyPressed,keyReleased etkeyTyped(seuleladernièrenousintéresseici).Pourforcerlefocussurunbouton,nousutilisonslaméthoderequestFocus.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrameimplementsKeyListener
{privatestaticintnBoutons=7;
publicMaFenetre()
{setTitle("SELECTIONSPARCLAVIER");
setSize(350,150);
Containercontenu=getContentPane();
380
contenu.setLayout(newFlowLayout());
addKeyListener(this);//attention:ajouteralafenetre,pas
aucontenu
boutons=newJButton[nBoutons];
for(inti=0;i<nBoutons;i++)
{boutons[i]=newJButton("BOUTON"+(i+1));
contenu.add(boutons[i]);
}
}
publicvoidkeyPressed(KeyEvente){}
publicvoidkeyReleased(KeyEvente){}
publicvoidkeyTyped(KeyEvente)
{charc=e.getKeyChar();
intnum=c-'0';
if((num>0)&&(num<=nBoutons))
boutons[num-1].requestFocus();
}
privateJButtonboutons[];
}
publicclassSelClav
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
381
141Miseenévidenced’uncomposantsélectionné
Afficherdansunefenêtreuncertainnombredeboutonsdecouleur jaune.Faireensortequelorsqu’unboutonprendlefocus,ilsecoloreenrouge.
Ici, nous faisons de la fenêtre l’écouteur des événements Focus générés par lesdifférents boutons. Nous redéfinissons les méthodes focusGained et focusLost demanièreàmodifiercommevoululacouleurdubouton.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
classMaFenetreextendsJFrameimplementsFocusListener
{privatestaticintnBoutons=8;
private static Color coulRepos = Color.yellow, coulSelec =
Color.red;
publicMaFenetre()
{setTitle("SELECTIONSCOLOREES");
setSize(350,150);
Containercontenu=getContentPane();
contenu.setLayout(newFlowLayout());
for(inti=0;i<nBoutons;i++)
382
{bouton=newJButton("BOUTON"+(i+1));
contenu.add(bouton);
bouton.addFocusListener(this);
bouton.setBackground(coulRepos);
}
}
publicvoidfocusGained(FocusEvente)
{Objectsource=e.getSource();
if(sourceinstanceofJButton)
{JButtonbSource=(JButton)source;
bSource.setBackground(coulSelec);
}
}
publicvoidfocusLost(FocusEvente)
{Objectsource=e.getSource();
if(sourceinstanceofJButton)
{JButtonbSource=(JButton)source;
bSource.setBackground(coulRepos);
}
}
privateJButtonbouton;
}
publicclassSelecCol
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
1.Bienqu’elle fournisse lemême résultat, il s’agit biend’uneméthodedifférente degetActionCommand de laclasseActionEvent.
383
Chapitre14
Lesapplets
Connaissancesrequises
•LaclasseJApplet;lesméthodesinit,start,stopetdestroy;legestionnairepardéfaut
•Écritured’unfichierHTMLpermettantdelanceruneapplet;informationscode,widthetheight
•Transmissiond’informationsàuneappletparlefichierHTMLetrécupérationparlaméthodegetParameter
•Transformationd’uneapplicationenuneapplet
384
142Comptagedesarrêtsd’uneapplet
Réaliser une applet affichant en permanence le nombre de fois où elle a étéinterrompue.
Lenombredefoisoùl’appletaétéinterrompuepeuts’obtenirencomptantlenombrede fois où saméthode stop a été appelée (avec la méthode start, on obtiendrait lamêmechoseàuneunitéprès).Uncompteurest initialiséà0danssaméthode init etincrémentéde1àchaqueappeldestop.D’autrepart,àchaqueappeldestop, il fautactualiserlecontenud’unobjetétiquette(JLabel)indiquantlavaleurdececompteur.Cetobjetestajoutéparadd aucontenude l’applet, sachantque songestionnairepardéfaut(BorderLayout)nousconvientici.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
publicclassCompteurextendsJApplet//nepasoublierpublic
{publicvoidinit()
{valeurCompteur=newJLabel(texte+compteur);
getContentPane().add(valeurCompteur);
}
publicvoidstop()
{compteur++;
valeurCompteur.setText(texte+compteur);
}
privateJLabelvaleurCompteur;
privateintcompteur=0;
privateStringtexte="Nombred'arrets=";
}
VoiciunexempledefichierHTMLpermettantdelancercetteapplet1soitauseind’unnavigateur,soitdansunepageWeb(limitéealorsiciàl’appletetnedisposantpasde
385
titre):
HTML>
<BODY>
<APPLET
CODE="Compteur.class"
WIDTH=250
HEIGHT=120
>
</APPLET>
</BODY>
</HTML>
Voiciunexempled’exécutiondansunvisualisateurd’applet:
386
143Dessindansuneapplet
Réaliser une applet qui affiche en permanence le dessin suivant (étoile dans uncercle)detaillefixe:
EcrireunexempledefichierHTMLdelancementdecetteapplet.
Afind’enassurerlapermanence,ledessinestréalisédansunpanneaudontonredéfinitlaméthodepaintComponent.Dans la méthode init de l’applet, on crée le panneau et on le rattache par add aucontenu de l’applet fourni par la méthode getContentPane (on procède exactementcommedansleconstructeurd’unefenêtre).Lesdimensionsdudessinsontdéfiniesparlesconstantesxc,yc(coordonnéesducentreducercle)etrayondelaclassePanneau.Letracédel’étoileestréaliséparuneboucledessinantsuccessivementchacundeses6segments.Lavariableanglecorrespondàl’anglequeformeavecl’axedesabcisseslerayonpassantparl’originedechacundessegments.
importjava.awt.*;
importjavax.swing.*;
387
publicclassAppEtoilextendsJApplet//nepasoublierpublic
{publicvoidinit()
{Containercontenu=getContentPane();
pan=newPanneau();
contenu.add(pan);
}
privatePanneaupan;
}
classPanneauextendsJPanel
{privatestaticintxc=80,yc=80,rayon=60;
publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
/*traceducercle*/
g.drawOval(xc-rayon,yc-rayon,2*rayon,2*rayon);
/*tracedes6segmentsdel'etoile*/
doubleangle,xd,xf,yd,yf;
inti;
{for(i=0,angle=Math.PI/6.;i<6;i++,angle+=Math.PI/3)
{xd=xc+rayon*Math.cos(angle);
yd=yc-rayon*Math.sin(angle);
xf=xc+rayon*Math.cos(angle+2*Math.PI/3);
yf=yc-rayon*Math.sin(angle+2*Math.PI/3);
g.drawLine((int)xd,(int)yd,(int)xf,(int)yf);
}
}
}
}
VoiciunexempledefichierHTMLdelancementdel’applet2:
<HTML>
<BODY>
<APPLETCODE="AppEtoil.class"WIDTH=200HEIGHT=150>
</APPLET>
</BODY>
</HTML>
Dans notre précédente solution, les coordonnées des segments sont recalculées à
388
chaqueappeldepaintComponent.Onpeutenréalitéprofiterdufaitquel’imageestdetaillefixepourn’effectuerqu’uneseulefoisl’essentieldescalculs,parexempledansleconstructeurdupanneau:
importjava.awt.*;
importjavax.swing.*;
publicclassAppEtoibextendsJApplet//nepasoublierpublic
{publicvoidinit()
{Containercontenu=getContentPane();
pan=newPanneau();
contenu.add(pan);
}
privatePanneaupan;
}
classPanneauextendsJPanel
{privatestaticintxc=80,yc=80,rayon=60;
publicPanneau()
{xd=newint[6];
yd=newint[6];
xf=newint[6];
yf=newint[6];
/* calculs des coordonnes des origines et extremites des 6
segments*/
doubleangle;
inti;
for(i=0,angle=Math.PI/6.;i<6;i++,angle+=Math.PI/3)
{xd[i]=(int)(xc+rayon*Math.cos(angle));
yd[i]=(int)(yc-rayon*Math.sin(angle));
xf[i]=(int)(xc+rayon*Math.cos(angle+2*Math.PI/3));
yf[i]=(int)(yc-rayon*Math.sin(angle+2*Math.PI/3));
}
}
publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
/*traceducercle*/
g.drawOval(xc-rayon,yc-rayon,2*rayon,2*rayon);
/*tracedes6segmentsdel'etoile*/
for(inti=0;i<6;i++)
389
g.drawLine(xd[i],yd[i],xf[i],yf[i]);
}
privateint[]xd,yd,xf,yf;
}
390
144Synthèse:dessinparamétrédansuneapplet
Réaliser une applet qui affiche en permanence un rectangle coloré dont lesdimensions et la couleur sont fournies par des paramètres figurant dans le fichierHTMLdelancement:
Le rectangle sera placé au centre de l’applet dont on supposera que la taillen’évolue pasa. Donner un exemple de fichier HTML permettant de lancer cetteapplet.
a.Lesvisualisateursd’appletautorisentcettemodificationdetaille,maispaslesnavigateurs.
LerectangleestdessinédansunpanneaudontonredéfinitlaméthodepaintComponentpourassurerlapermanencedudessin.Dansl’objetapplet,onrécupèrelesvaleursdesparamètresfigurantdanslefichierHTML.Rappelonsquecesdernierssontidentifiésparunnom(chaînedanslaquellelacassen’estpassignificative)etunevaleur(chaîneégalement).Onrécupèrelavaleurd’unparamètreàl’aidedelaméthodegetParameteràlaquelleonfournitenargumentlenomduparamètrevoulu.En cas de besoin, les dimensions de l’applet (de nom width et height) peuventégalementêtrerécupérésdecettemanière.C’estcequinouspermeticidecalculerlapositiondurectangledanslafenêtredel’applet.
391
SilesvaleursdecesparamètresnesontpasprésentesdanslefichierHTMLousiellesnesontpasconvertiblesenunentier,nousattribuonsaurectangledesdimensionspardéfaut (celles de l’applet ne peuvent pas être incorrectes, sinon l’applet nes’exécuteraitpas).La "valeur" d’une couleur est définie par une chaîne représentant son nom (rouge,vert…).OnluifaitcorrespondreunobjetdetypeColoràl’aidededeuxtableaux,l’unde type String contenant les noms de couleur, l’autre de type Color contenant lescouleursassociées.La communication entre l’applet et le panneau se fait par des méthodes d’accès del’applet.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
publicclassAppRectextendsJApplet//nepasoublierpublic
{StringnomsCouleurs[]={"rouge","vert","bleu","jaune"};
Color couleurs[] = {Color.red, Color.green, Color.blue,
Color.yellow};
publicvoidinit()
{Containercontenu=getContentPane();
pan=newPanneau(this);
contenu.add (pan) ; // avec le gestionnaire BorderLayout, le
panneau
//occupetoutelafenetre
/*recuperationparametresdimensionapplet,dimensionrectangle,
couleur*/
StringchLargeurApplet=getParameter("width");
StringchHauteurApplet=getParameter("height");
StringchLargeurRect=getParameter("Largeur");
StringchHauteurRect=getParameter("Hauteur");
try
{largeurApplet=Integer.parseInt(chLargeurApplet);
hauteurApplet=Integer.parseInt(chHauteurApplet);
largeurRect=Integer.parseInt(chLargeurRect);
hauteurRect=Integer.parseInt(chHauteurRect);
}
catch(NumberFormatExceptionex)
{/*onattribuedesdimensionspardefautpourlerectangle*/
/*(cellesdel'appletsonttoujoursbonnes)*/
largeurRect=80;hauteurRect=50;
392
}
nomCouleur=getParameter("Couleur");
couleur=Color.black;//couleurpardefaut
for(inti=0;i<nomsCouleurs.length;i++)
{if(nomCouleur.equals(nomsCouleurs[i]))couleur=couleurs[i];
}
}
publicintgetLargeurApplet(){returnlargeurApplet;}
publicintgetHauteurApplet(){returnhauteurApplet;}
publicintgetLargeurRect(){returnlargeurRect;}
publicintgetHauteurRect(){returnhauteurRect;}
publicColorgetCouleur(){returncouleur;}
privatePanneaupan;
private int largeurApplet, hauteurApplet, largeurRect,
hauteurRect;
privateStringnomCouleur;
privateColorcouleur;
}
classPanneauextendsJPanel
{publicPanneau(AppRectap)
{this.ap=ap;
}
publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
intx=(ap.getLargeurApplet()-ap.getLargeurRect())/2;
inty=(ap.getHauteurApplet()-ap.getHauteurRect())/2;
g.setColor(ap.getCouleur());
g.fillRect(x,y,ap.getLargeurRect(),ap.getHauteurRect());
}
AppRectap;
}
Voiciun fichierHTMLde lancementdecetteapplet3 dansune fenêtrededimensions350×120avecunrectanglededimensions300×50etdecouleurrouge:
<HTML>
<BODY>
<APPLETCODE="AppRect.class"WIDTH=350HEIGHT=120>
<PARAMNAME="Largeur"VALUE="300">
<PARAMNAME="Hauteur"VALUE="50">
<PARAMNAME="Couleur"VALUE="rouge">
393
</APPLET>
</BODY>
</HTML>
Ici, lesdimensionsdurectanglesontrecalculéesàchaqueappeldepaintComponent.Cecalculpourraitêtre faitune foispour toutes,parexempledans laméthode init, àconditiontoutefoisquecesoitavantlepremieraffichagedupanneau.
394
145Synthèse:tracédecourbedansuneapplet
Réaliseruneappletpermettantdereprésentersousformed’unecourbeunesuitedevaleurs entières positives ou nulles figurant en paramètres dans le fichierHTMLcorrespondant,commedanscetexemple:
Le titre sera fourni en paramètre. Le nombre de valeurs devra pouvoir êtrequelconque et sera également fourni en paramètre. L’applet affichera la valeurmaximale.DonnerunexempledefichierHTMLpermettantdelancercetteapplet.
Nous introduisons dans la fenêtre de notre applet un panneau pour la courbe et unchamp de texte pour le titre. Nous conservons le gestionnaire par défaut(BorderLayout)enplaçantletitreenhaut("North")etlepanneauaucentre.LesparamètresdufichierHTMLsontrécupérésclassiquementdanslaméthodeinitenutilisant getParameter. Nous supposons ici que les noms de ces paramètres sontTITRE,NB_VALEURS,VALEUR1,VALEUR2,VALEUR3…Notez que les noms desdifférentesvaleurssontformésd’unmêmepréfixe(iciVALEUR)suividu"numéro"devaleur. Ceci nous permet de traiter un nombre quelconque de valeurs. Ici, nous netraitons pas les éventuelles exceptions que pourraient déclencher des valeurs
395
incorrectesoumanquantes;l’appletsetermineraitalorssimplementavecunmessaged’erreur.LaméthodepaintComponentdupanneauendéterminelatailleàl’aidedelaméthodegetSize. Les valeurs à tracer sont obtenues par la méthode d’accès getValeurs del’applet.Lescoordonnéesdesdifférentspoints sont alorscalculéesen tenantcompted’unfacteurd’échelle(echelle)déterminédemanièreque:
•lepointcorrespondantàlaplusgrandevaleurs’affichetoutenhautdupanneau,
•lepremierpoints’afficheàl’extrémitégauchedupanneau,ledernieràl’extrémitédroite.
Notezquenousemployonsdesvariablesde typedoublepouréviterune imprécisionrésultantdedivisiond’entiers.Notezégalementqu’ilestnécessaired’inverserlesordonnéesafind’obtenirunaxedesydirigéverslehaut.
importjava.awt.*;
importjavax.swing.*;
publicclassAppCourbextendsJApplet//nepasoublierpublic
{publicvoidinit()
{/*lesdeuxcomposantsdel'applet:champtexteetpanneau*/
Containercontenu=getContentPane();
JLabelchampTitre=newJLabel(getParameter("TITRE"));
contenu.add(champTitre,"North");//titreenhaut
pan=newPanneau(this);
contenu.add(pan);//panneaupourlacourbeau
centre
/* recuperation des parametres HTML : nombre de valeurs et
valeurs*/
nValeurs=Integer.parseInt(getParameter("NB_VALEURS"));
if(nValeurs<=1)System.exit(-1);//aumoins2valeurspour
unecourbe
valeurs=newint[nValeurs];
for(inti=0;i<nValeurs;i++)
valeurs[i]=Integer.parseInt(getParameter("VALEUR"+(i+1)));
}
publicint[]getValeurs()
{returnvaleurs;
}
privatePanneaupan;
396
privateintnValeurs;
privateintvaleurs[];
}
classPanneauextendsJPanel
{publicPanneau(AppCourbap)
{this.ap=ap;
}
publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
/*determinationdeladimensiondupanneau*/
DimensiondimPanneau=getSize();
inthauteur=dimPanneau.height;
intlargeur=dimPanneau.width;
/*recuperationdesvaleurs*/
int[]valeurs=ap.getValeurs();
intnValeurs=valeurs.length;
/*recherchedelavaleurmaximale*/
intvalMax=valeurs[0];
for(inti=1;i<nValeurs;i++)
if(valeurs[i]>valMax)valMax=valeurs[i];
/*tracedelacourbepointparpoint*/
doubleecart=(double)largeur/(nValeurs-1);//onanValeurs>1
doubleechelle=(double)hauteur/valMax;
doublexDeb=0,yDeb=hauteur-valeurs[0]*echelle;
doublexFin,yFin;
for(inti=1;i<nValeurs;i++)
{xFin=xDeb+ecart;
yFin=hauteur-valeurs[i]*echelle;
g.drawLine((int)xDeb,(int)yDeb,(int)xFin,(int)yFin);
xDeb=xFin;
yDeb=yFin;
}
}
AppCourbap;
}
VoiciunexempledefichierHTMLpermettantd’exploiterceprogramme4(ilfournitlacourbeprésentéedansl’énoncé):
<HTML>
397
<BODY>
<APPLETCODE="AppCourb.class"WIDTH=250HEIGHT=120>
<PARAMNAME="TITRE"VALUE="Evolutiondesventes">
<PARAMNAME="NB_VALEURS"VALUE="6">
<PARAMNAME="VALEUR1"VALUE="175">
<PARAMNAME="VALEUR2"VALUE="288">
<PARAMNAME="VALEUR3"VALUE="352">
<PARAMNAME="VALEUR4"VALUE="181">
<PARAMNAME="VALEUR5"VALUE="135">
<PARAMNAME="VALEUR6"VALUE="285">
</APPLET>
</BODY>
</HTML>
Ici, les coordonnées du tracé sont calculées à chaque appel de paintComponent. Sil’onutiliseunvisualisateurqui autorise le redimensionnementde l’applet,onpourraainsivoirletracés’adapteràlataillecourantedelafenêtre.Iln’eniraitpasainsisil’ondéterminaitcesdimensionsdanslaméthodeinit.
398
146Différencesentreappletetapplication
Adapter l’exercice103du chapitre8 demanière que l’utilisateur puisse dessinerdansuneappletetnonplusdansunefenêtre.
Ilsuffitd’adapterlecodeentenantcomptedesquelquesremarquessuivantes:
•supprimerlaméthodemain(sionlaconservait,elleneseraitpasappeléelorsdulancementducodedepuisunfichierHTML);
• transformer la classe fenêtre (MaFenetre) enune classe (iciDesVol) dérivée deJApplet;
• transposerdans laméthode initde laclasseDesVol lesactions réaliséesdans leconstructeurdelafenêtreMaFenetre;
•supprimerlesappelsàsetTitleetsetSizequin’ontplusderaisond’êtrepouruneapplet(pasdetitre,dimensionsdéfiniesparlesparamètresWIDTHetHEIGHTdufichierHTMLdelancement).
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
publicclassDesVolextendsJApplet//nepasoublierpublic
{publicvoidinit()
{pan=newPanneau();
pan.addMouseListener(pan);
getContentPane().add(pan);
}
privatePanneaupan;
}
classPanneauextendsJPanelimplementsMouseListener
{publicvoidpaintComponent(Graphicsg)
{super.paintComponent(g);
399
enCours=false;
}
publicvoidmouseClicked(MouseEvente)
{intxFin=e.getX();yFin=e.getY();
if(enCours){Graphicsg=getGraphics();
g.drawLine(xDeb,yDeb,xFin,yFin);
g.dispose();
}
xDeb=xFin;yDeb=yFin;
enCours=true;
}
publicvoidmousePressed(MouseEvente){}
publicvoidmouseReleased(MouseEvente){}
publicvoidmouseEntered(MouseEvente){}
publicvoidmouseExited(MouseEvente){}
privatebooleanenCours=false;
privateintxDeb,yDeb,xFin,yFin;
}
Àtitreindicatif,voiciunfichierHTMLtrèssimple(DesVol.html)permettantdelancercetteapplet:
<HTML>
<BODY>
<APPLET
CODE="DesVol.class"
WIDTH=350
HEIGHT=100
>
</APPLET>
</BODY>
</HTML>
1.CertainsnavigateursemploientlabaliseOBJECTouEMBEDàlaplacedelabaliseAPPLET.2.CertainsnavigateursemploientlabaliseOBJECTouEMBEDàlaplacedelabaliseAPPLET.3.CertainsnavigateursemploientlabaliseOBJECTouEMBEDàlaplacedelabaliseAPPLET.4.CertainsnavigateursemploientlabaliseOBJECTouEMBEDàlaplacedelabaliseAPPLET.
400
Chapitre15
Lesfluxetlesfichiers
Connaissancesrequises
•Notiondeflux;fluxd’entrée,fluxdesortie;fluxbinaire,fluxtexte
•Créationséquentielled’unfichierbinaire;classesOutputStream,FileOutputStreametDataOutputStream
•Listeséquentielled’unfichierbinaire;classesInputStream,FileInputStreametDataInputStream
•Accèsdirectàunfichierbinaire;classesRandomAccessFile;actionsurlepointeurdefichier
•Créationd’unfichiertexte;classePrintWriter
•Lectured’unfichiertexte;classesFileReader,BufferedReaderetStringTokenizer
•GestiondesfichiersaveclaclasseFile
401
147Créationséquentielled’unfichierbinaire
Écrire un programme permettant de créer séquentiellement un fichier binairecomportantpourdifférentespersonnes les informationssuivantes :nom,prénometannéedenaissance.Ledialoguedesaisiedel’informations’effectueraenfenêtreconsolecommedanscetexemple:
Nomdufichieracreer:
e:\repert
nom1:Carre
Prenom:Thibault
anneenaissance:1997
.....
nom5:Mitenne
Prenom:Thomas
anneenaissance:2001
nom6:
****fincreationfichier****
Onproposeradeuxsolutions:1. Les informations relatives au nom et au prénom seront conservées dans lefichier sous la forme d’une suite de 20 caractères (comportant d’éventuelsespacesàlafin).2.Cesmêmesinformationsserontconservéessous laformed’unechaînecodéedansleformatUTFa;aucunecontrainteneporterasurleurlongueur.
a.Ceformat(UnicodeTextFormat)permetdecoderunechaînesousformed’unesuited’octetsennombrevariable(chaquecaractèreétantcodésurunà troisoctets).LaméthodewriteUTFde laclasseDataOutputStream réalisecettetransformationd’unechaîneenunesuitedecaractèresUTF.
NousutiliseronsladémarchelaplusclassiquequiconsisteàexploiterlesméthodesdelaclassefluxDataOutputStream.Pourcefaire,nousassocieronsunobjetdecetype(nommé sortie) à un fichier dont le nom est fourni par l’utilisateur dans la chaîne
402
nomFichier:DataOutputStreamsortie=newDataOutputStream
(newFileOutputStream(nomFichier));
LesvariableschNometchPrenomserventàlirelesinformationsnometprénomsousformedechaînesdecaractères.Nousen transféronsensuitechacundescaractères (àconcurrencede20)dansdestableauxde20caractèresnometprenom,préalablementremplisavecdesespaces.L’écrituredans le fichierest réaliséeà l’aidedesméthodeswriteChar (écritured’uncaractère)etwriteInt(écritured’unentier)delaclasseDataOutputStream.importjava.io.*;
publicclassCrFich
{publicstaticvoidmain(Stringargs[])throwsIOException
{finalintlongMaxNom=20;
finalintlongMaxPrenom=20;
StringchNom,chPrenom;
char[]nom=newchar[longMaxNom];
char[]prenom=newchar[longMaxPrenom];
intannee;
StringnomFichier;
System.out.println("Nomdufichieracreer:");
nomFichier=Clavier.lireString();
DataOutputStreamsortie=newDataOutputStream
(newFileOutputStream(nomFichier));
inti;
intnum=0;//pourcompterlesdifferentsenregistrements
while(true)//ons'arreterasurnomvide
{/*lectureinfos*/
num++;
System.out.print("nom"+num+":");
chNom=Clavier.lireString();
if(chNom.length()==0)break;
System.out.print("Prenom:");
chPrenom=Clavier.lireString();
System.out.print("anneenaissance:");
annee=Clavier.lireInt();
/* transfert nom et prenom dans tab de char termines par des
espaces*/
for(i=0;i<longMaxNom;i++)nom[i]='';
403
for(i=0;i<longMaxPrenom;i++)prenom[i]='';
for(i=0;(i<chNom.length())&&(i<longMaxNom);i++)
nom[i]=chNom.charAt(i);
for(i=0;(i<chPrenom.length())&&(i<longMaxPrenom);i++)
prenom[i]=chPrenom.charAt(i);
/*ecriturefichier*/
for(i=0;i<longMaxNom;i++)sortie.writeChar(nom[i]);
for(i=0;i<longMaxPrenom;i++)sortie.writeChar(prenom[i]);
sortie.writeInt(annee);
}
sortie.close();
System.out.println("****fincreationfichier****");
}
}
1.LaclausethrowsIOExceptionfigurantdanslaméthodemainestnécessaire,dèslors qu’on n’y traite pas les exceptions susceptibles d’être déclenchées par lesméthodesdelaclasseDataOutputstream.2.Plutôtqued’écrireunàunchacundescaractèresdenometdeprenom,onauraitpuespérerappliquerdirectementàchNometchPrenomlaméthodewriteCharsquiécrittouslescaractèresd’unechaîne.Cependant,cettedémarchenecorrepondpasàlademandedel’énoncé(informationsdetaillefixedanslefichier);deplus,ellenepermettraitpasderelireultérieurementlefichier(àmoinsdeconnaîtreparailleursleslongueursdechacunedesinformationsyfigurant!).
Commeprécédemment, nous créonsunobjet de typeDataOutputStream.Mais, cettefois,nouspouvonsappliquerlaméthodewriteUTFauxchaînescorrespondantaunometauprénom.importjava.io.*;
publicclassCrFich2
{publicstaticvoidmain(Stringargs[])throwsIOException
{StringchNom,chPrenom;
intannee;
StringnomFichier;
404
System.out.println("Nomdufichieracreer:");
nomFichier=Clavier.lireString();
DataOutputStreamsortie=newDataOutputStream
(newFileOutputStream(nomFichier));
inti;
intnum=0;//pourcompterlesdifferentsenregistrements
while(true)//ons'arreterasurnomvide
{/*lectureinfos*/
num++;
System.out.print("nom"+num+":");
chNom=Clavier.lireString();
if(chNom.length()==0)break;
System.out.print("Prenom:");
chPrenom=Clavier.lireString();
System.out.print("anneenaissance:");
annee=Clavier.lireInt();
/*ecriturefichier*/
sortie.writeUTF(chNom);
sortie.writeUTF(chPrenom);
sortie.writeInt(annee);
}
sortie.close();
System.out.println("****fincreationfichier****");
}
}
Cettesecondedémarchepeutparaîtreplussouplequelapremièrepuisqu’ellen’imposeaucunelimiteàlatailledeschaînesfournies.Néanmoins,elleprésentel’inconvénientdeneplusêtreadaptéeàl’exploitationultérieuredufichierenaccèsdirect.
405
148Listeséquentielled’unfichierbinaire
Écrireunprogrammepermettantdelisterenfenêtreconsolelecontenud’unfichierbinairetelqueceluicrééparl’exercice.Onproposeradeuxsolutionscorrespondantauxdeuxsituations:1. Les informations relatives au nom et au prénom ont été enregistrées dans lefichier sous la forme d’une suite de 20 caractères (comportant d’éventuelsespacesàlafin).2.Cesmêmesinformationsontétéenregistréessouslaformed’unechaînecodéedansleformatUTF;aucunecontrainteneporterasurleurlongueur.
NousexploitonslesméthodesdelaclassefluxDataInputStream.Pource faire,nousassocionsunobjetdece type(nomméentree)àunfichierdont lenomest fourniparl’utilisateurdanslachaînenomFichier:DataInputStreamentree=newDataInputStream
(newFileInputStream(nomFichier));
Les informations relatives au nom et au prénom sont lues dans des tableaux de 20caractères nom et prenom à l’aide de la méthode readChar de la classeDataInputStream.Lagestiondelafindefichierestréaliséeeninterceptantl’exceptionEOFException :la boucle de lecture des informations est contrôlée par un indicateur booléen eofinitialiséàfalseetmisàtrueparlegestionnaired’exception.
importjava.io.*;
publicclassLecFich
{
publicstaticvoidmain(Stringargs[])throwsIOException
{finalintlongMaxNom=20;
finalintlongMaxPrenom=20;
StringchNom,chPrenom;
406
char[]nom=newchar[longMaxNom];
char[]prenom=newchar[longMaxPrenom];
intannee;
inti;
StringnomFichier;
System.out.println("Nomdufichieralister:");
nomFichier=Clavier.lireString();
DataInputStreamentree=newDataInputStream
(newFileInputStream(nomFichier));
System.out.println("****Listedufichier****");
boolean eof = false ; // sera mis a true par gestionnaire
exceptionEOFile
while(!eof)
{try
{/*lectureinfos*/
for(i=0;i<longMaxNom;i++)nom[i]=entree.readChar();
for (i=0 ; i<longMaxPrenom ; i++) prenom[i] = entree.readChar
();
annee=entree.readInt();
/*affichageinfos*/
for(i=0;i<longMaxNom;i++)System.out.print(nom[i]);
System.out.print("");
for(i=0;i<longMaxPrenom;i++)System.out.print(prenom[i]);
System.out.print("");
System.out.println(annee);
}
catch(EOFExceptione)
{eof=true;
}
}
entree.close();
System.out.println("****finlistefichier****");
}
}
Àtitreindicatif,voicil’alluredesrésultatsfournisparceprogramme:Nomdufichieralister:
e:\repert
****Listedufichier****
CarreThibault1997
407
DuboisLouis1975
DutroncJeanPhilippe1958
DucheneAlfred1994
MitenneThomas2001
***finlistefichier****
Comme précédemment, on fait appel à un objet de typeDataInputStream. Mais lesinformationsrelativesaunometauprénomsontluesdirectementàl’aidedelaméthodereadUTF.Lagestiondelafindefichiersedérouletoujoursdelamêmemanière.
importjava.io.*;
publicclassLecFich2
{
publicstaticvoidmain(Stringargs[])throwsIOException
{finalintlongMaxNom=20;
finalintlongMaxPrenom=20;
StringchNom,chPrenom;
intannee;
inti;
StringnomFichier;
System.out.println("Nomdufichieralister:");
nomFichier=Clavier.lireString();
DataInputStreamentree=newDataInputStream
(newFileInputStream(nomFichier));
System.out.println("****Listedufichier****");
boolean eof = false ; // sera mis a true par gestionnaire
exceptionEOFile
while(!eof)
{try
{/*lectureinfos*/
chNom=entree.readUTF();
chPrenom=entree.readUTF();
annee=entree.readInt();
/*affichageinfos*/
System.out.print(chNom+"");
System.out.print(chPrenom+"");
System.out.println(annee);
408
}
catch(EOFExceptione)
{eof=true;
}
}
entree.close();
System.out.println("****finlistefichier****");
}
}
Lesrésultatsseprésententalorssouscetteforme:
Nomdufichieralister:
e:\reputf
****Listedufichier****
CarreThibault1997
DuboisLouis1975
DutroncJeanPhilippe1958
DucheneAlfred1994
MitenneThomas2001
****finlistefichier****
409
149Synthèse:consultationd’unrépertoireenaccèsdirect
Réaliserunprogrammepermettantdeconsulterunfichierdutypedeceluicrééparla première solution à l’exercice. Le dialogue s’opérera à travers des contrôlesdisposésdansunefenêtrecommeillustréeci-aprèsa:
L’utilisateurpourraagirindifféremmentsurleschampsdetexteindiquantlenomdefichier ou le nom d’enregistrement. On signalera par des boîtes de message leserreurssuivantes:
•fichierinexistant,
•informationdenumérod’enregistrementnonnumérique,négativeousupérieureàlatailledufichier.
Lorsqu’unfichierseracorrectementouvert, sonnoms’afficheradans le titrede lafenêtre.
Note:pourquelescontrôlessoientdisposéscommedansnotreexemple,onpourrautiliser un gestionnaire de mise en forme de type GridLayout créé par newGridLayout(5,2).
a.OnpourrautiliserungestionnairedemiseenformedetypeGridBag.
410
Les dimensions des tableaux de caractères sont définies par des constantessymboliques LG_NOM et LG_PRENOM. Il en va de même pour la taille d’unenregistrement(TAILLE_ENREG)dontonnoteraquelecalculdoittenircomptedufaitquelescaractèressontenregistrésenbinaireetqu’ilsoccupentdonc2octets.Ladispositiondesdifférentscontrôlesneposepasdeproblèmeparticulier.Onnoteraque,avecungestionnairedetypeGridLayout,leconteneurestrempliligneparligne,suivant l’ordredans lequel ils sont ajoutés.Nousutilisonsdes champsde textepourtouteslesinformationsmaisseulslesdeuxpremierssont"éditables".Nous écoutons les événementsFocus etAction des deux champs de saisie (nom defichier et numéro d’enregistrement). Deux méthodes de service nomméesnouveauFichieretnouvelEnregnousévitentdedupliquercertainesinstructions.Lademanded’ouvertured’unnouveaufichierentraînetoutd’abordlafermeturedetoutautrefichieréventuellementouvert.Puis,nousvérifionsl’existencedufichierdenomindiquéentraitantconvenablementl’exceptiongénéréeparsademanded’ouvertureencas d’inexistence. Lorsque les choses se sont convenablement déroulées, nousdéterminons la taille du fichier en octets (méthode length) et nous déterminons lenombred’enregistrementscorrespondants.Danslademanded’unnouvelenregistrement,nousvérifionsque:
•l’informationfourniepeutêtreconvenablementconvertieenunentier,
•qu’ellepossèdeunevaleurcompatibleaveclatailledufichier.Silenumérod’enregistrementestconvenable,nouspositionnonslepointeuràl’endroitcorrespondant du fichier (méthode seek). Nous lisons les différentes informationsvouluesetnous les affichonsdans les champsappropriés.Notezque les tableauxdecaractèresconstituantlenometleprénomdoiventêtreconvertisenchaînes;pourcefaire,nousutilisonsunconstructeurdelaformeString(char[]).
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
importjava.io.*;
class MaFenetre extends JFrame implements ActionListener,
FocusListener
{privatestaticfinalintLG_NOM=20,LG_PRENOM=20;
private static final int TAILLE_ENREG = 2*LG_NOM + 2*LG_PRENOM +
4;
private static final String titreFenetre = "Consultation
repertoire";
411
publicMaFenetre()
{nom=newchar[LG_NOM];
prenom=newchar[LG_PRENOM];
setTitle(titreFenetre);
setSize(400,200);
Containercontenu=getContentPane();
contenu.setLayout(newGridLayout(5,2));
labNomFichier=newJLabel(etiqNomFichier);
contenu.add(labNomFichier);
txtNomFichier=newJTextField(20);
contenu.add(txtNomFichier);
txtNomFichier.addActionListener(this);
txtNomFichier.addFocusListener(this);
labNumEnreg=newJLabel(etiqNumEnreg);
contenu.add(labNumEnreg);
txtNumEnreg=newJTextField(20);
contenu.add(txtNumEnreg);
txtNumEnreg.addActionListener(this);
txtNumEnreg.addFocusListener(this);
labNom=newJLabel(etiqNom);
contenu.add(labNom);
txtNom=newJTextField(20);txtNom.setEditable(false);
contenu.add(txtNom);
labPrenom=newJLabel(etiqPrenom);
contenu.add(labPrenom);
txtPrenom=newJTextField(20);txtPrenom.setEditable(false);
contenu.add(txtPrenom);
labAnnee=newJLabel(etiqAnnee);
contenu.add(labAnnee);
txtAnnee=newJTextField(20);txtAnnee.setEditable(false);
contenu.add(txtAnnee);
}
publicvoidactionPerformed(ActionEvente)
{Objectsource=e.getSource();
if(source==txtNomFichier)nouveauFichier();
if(source==txtNumEnreg)nouvelEnreg();
}
publicvoidfocusGained(FocusEvente)
412
{}
publicvoidfocusLost(FocusEvente)
{Objectsource=e.getSource();
if(source==txtNomFichier)nouveauFichier();
if(source==txtNumEnreg)nouvelEnreg();
}
privatevoidnouveauFichier()
{try
{if(fichierOuvert)
{fichier.close();
fichierOuvert=false;
setTitle(titreFenetre);
}
nomFichier=txtNomFichier.getText();
fichier=newRandomAccessFile(nomFichier,"r");
}
catch(IOExceptione)//erreurouverture
{JOptionPane.showMessageDialog(null,"FICHIERINEXISTANT");
txtNomFichier.setText("");
return;
}
fichierOuvert=true;
setTitle(titreFenetre+""+nomFichier);
try
{tailleFichierOctets=fichier.length();
tailleFichierEnreg=tailleFichierOctets/TAILLE_ENREG;
}
catch(IOExceptione){}
txtNumEnreg.setText("");txtNom.setText("");
txtPrenom.setText("");txtAnnee.setText("");
}
privatevoidnouvelEnreg()
{if(!fichierOuvert)
{JOptionPane.showMessageDialog(null,"Pasdefichierouvert");
txtNumEnreg.setText("");
return;
}
/*lecturenumeroenregistrementetcontrolesvalidite*/
413
StringchNumEnreg=txtNumEnreg.getText();
booleanconverti=false;
try
{num=Integer.parseInt(chNumEnreg);
converti=true;
}
catch(NumberFormatExceptione){}
if(!converti||(num<=0)||(num>tailleFichierEnreg))
{JOptionPane.showMessageDialog(null,"Numeroenregincorrect");
txtNumEnreg.setText("");txtNom.setText("");
txtPrenom.setText("");txtAnnee.setText("");
return;
}
/*numerocorrect-lecturedel'enregistrementcorrespondant*/
try
{numEnreg=num;
fichier.seek(TAILLE_ENREG*(numEnreg-1));
for(inti=0;i<LG_NOM;i++)nom[i]=fichier.readChar();
for (int i=0 ; i<LG_PRENOM ; i++) prenom[i] =
fichier.readChar();
annee=fichier.readInt();
/*conversiondesinformationsenchaineetaffichage*/
StringchNom=newString(nom);
StringchPrenom=newString(prenom);
StringchAnnee=String.valueOf(annee);
txtNom.setText(chNom);
txtPrenom.setText(chPrenom);
txtAnnee.setText(chAnnee);
}
catch(IOExceptione){}
}
privatebooleanfichierOuvert=false;
privateStringnomFichier;
privateRandomAccessFilefichier;
privatelongtailleFichierEnreg,tailleFichierOctets;
privateintnumEnreg,num;
privatechar[]nom,prenom;
privateintannee;
private JLabel labNomFichier, labNumEnreg, labNom, labPrenom,
labAnnee;
414
private JTextField txtNomFichier, txtNumEnreg, txtNom, txtPrenom,
txtAnnee;
static private String etiqNomFichier = "Nom
fichier:",
etiqNumEnreg="Numeroenregistrement:",
etiqNom="Nom:",
etiqPrenom="Prenom:",
etiqAnnee="Anneenaissance:",
}
publicclassListAD
{publicstaticvoidmain(Stringargs[])
{MaFenetrefen=newMaFenetre();
fen.setVisible(true);
}
}
1.Envertudesrèglesrelativesàlaredéfinitiond’uneméthode,iln’estpaspossibledementionnerdeclausethrowsIOExceptiondanslesméthodesactionPerformedoufocusLost. Dans ces conditions, il est nécessaire d’y traiter (ici artificiellement)l’exceptionIOException.2. On constate qu’en cas d’anomalie (fichier inexistant, numéro d’enregistrementincorrect),onobtientdeuxfoisl’affichagedumessagecorrespondant.Ceciprovientdelamiseàblancdeschampscorrespondants.Parsoucidesimplicité,nousn’avonspas cherché à régler le problème (par exemple, en recourant à des indicateursbooléens).
415
150Synthèse:listed’unfichiertexteavecnumérotationdeslignes
Écrireunprogrammequilisteenfenêtreconsolelecontenud’unfichiertexteenennumérotantleslignes.Onprévoira4caractèrespourl’affichagedunumérodeligne.Les lignes de plus de 60 caractères seront affichées sur plusieurs lignes d’écrancommedanscetexemple
Donnezlenomdufichiertextealister:e:\book\essai.txt
1Ceciestlapremiereligned'unexempledefichiertexte
2Ilcontientdeslignesdechiffresdelongueursvariables
dontunede59caracteres,unede60caracteresetunede61
caracteres
312345678901234567890
4
123456789012345678901234567890123456789012345678901234567890
512345678901234567890123456789012345678901234567890123456789
6
123456789012345678901234567890123456789012345678901234567890
1
71234567890123456789012345678901234567890
8lalignesuivanteestvide
9
10lesdeuxlignessuivantessontegalementvides
11
12
13Ceciestladernierelignedufichier
***finlistefichier***
Rappelonsque,pourlalectured’unfichiertexte,iln’existepasdeclasseparfaitementsymétrique de la classe PrintWriter. Il faut se contenter de la classe FileReader(symétrique de FileWriter, classe plus rudimentaire que PrintWriter) qu’on coupleavec la classeBufferedReader, laquelle dispose d’uneméthode readLine de lectured’une ligne.Nous créonsdoncunobjet de ce typenomméentree en procédant ainsi
416
(nomfichétantlachaînecorrespondantaunomdufichier):BufferedReader entree = new BufferedReader (new FileReader
(nomfich));
LaméthodereadLinede laclasseBufferedReader fournituneréférenceàunechaînecorrespondant à une ligne du fichier. Si la fin de fichier a été atteinte avant que lalecteur n’ait commencé, autrement dit si aucun caractère n’est disponible (pasmêmeunefindeligne!),readLinefournitlavaleurnull.Ilestdoncpossibledeparcourirlesdifférenteslignesdufichier,sansavoirbesoinderecouriràlagestiondesexceptions.En ce qui concerne l’affichage du numéro de ligne (numLigne), il est nécessaire deconvertir l’entier le représentant en une suite de 4 caractères. Pour ce faire, nousemployonsuntableaude4caractèresnommécharNumLignequenousinitialisonsavecdes caractères "espace", avant d’y introduire, à partir de la fin, les caractères de lachaîneobtenueparconversiondelavaleurdenumLigne.La gestion des lignes de plus de 60 caractères se fait simplement en affichant unchangementdeligneetunesuitede4+1espaces.
importjava.io.*;
publicclassListText
{publicstaticvoidmain(Stringargs[])throwsIOException
{ final int longNumLigne = 4 ; // nombre de caracteres utilises
pour
//afficherlenumerodeligne
finalintnbCarParLigne=60;
Stringnomfich;
Stringligne;//lignecourantedufichiertexte
char charNumLigne[] = new char[longNumLigne] ; // pour les
caracteres
//dunumerodeligne
System.out.print("Donnezlenomdufichiertextealister:");
nomfich=Clavier.lireString();
BufferedReader entree = new BufferedReader (new FileReader
(nomfich));
intnumLigne=0;
do
{/*lectured'unelignedufichier*/
ligne=entree.readLine();
if(ligne==null)break;
numLigne++;
/* determination des caracteres correspondant au numero de
ligne*/
417
Stringch=String.valueOf(numLigne);
inti,j;//pourparcourirlenumerodeligne
for(i=0;i<longNumLigne-ch.length();i++)charNumLigne[i]='
';
for (j=0 ; i<longNumLigne ; i++, j++) charNumLigne[i] =
ch.charAt(j);
/*affichagenumerodelignesuivid'unespace*/
for (i=0 ; i<longNumLigne ; i++) System.out.print
(charNumLigne[i]);
System.out.print('');
/*affichagelignecourante*/
intn=0;//pourparcourirlalignecourante
while(n<ligne.length())
{if((n!=0)&&(n%nbCarParLigne==0))/*onchangedeligne
*/
{System.out.println();
for(intk=0;k<longNumLigne+1;k++)
System.out.print('');
}
System.out.print(ligne.charAt(n));
n++;
}
System.out.println();
}
while(ligne!=null);
entree.close();
System.out.println("***finlistefichier***");
}
}
418
151Listed’unrépertoire
Écrireunprogrammequiaffichelecontenud’unrépertoire(dontlenomestfourniauclavier), enprécisantpourchaquenoms’il s’agitd’un sous-répertoireoud’unfichier;danscederniercas,ilenfourniraégalementlatailleenoctets.
nomdurepertoire:e:\truc
Nomincorrect(inexistantounonrepertoire)
nomdurepertoire:e:\book\exosjav
evbn.fmFICHIER84992octets
control.fmFICHIER96256octets
diversREPERTOIRE
menuac.fmFICHIER112640octets
.....
classesREPERTOIRE
essai.txtFICHIER5120octets
fichiers.fmFICHIER82944octets
ap.fmFICHIER35840octets
IlnoussuffitderecourirauxpossibilitésoffertesparlaclasseFile.Plusprécisément,àpartirdunomfournipar l’utilisateurdans lachaînenomRepert,nouscréonsunobjetobjRepdetypeFile:objRep=newFile(nomRepert);
La méthode isDirectory nous permet de savoir si ce nom correspond bien à unrépertoire.Notezqu’iln’estpasnécessaireiciderecouriràlaméthodeexists,danslamesureoùnousn’avonspascherchéàdistinguerlecasd’unnomnedésignantpasunrépertoireducasd’unnominexistant.Lorsque le nom correspond bien à un répertoire, nous faisons appel à la méthodelistFiles qui nous fournit un tableau d’objets de type File, chaque élémentcorrespondantàundesmembresdurépertoire.Ilnoussuffitalorsd’appliqueràchacund’entreeuxlesméthodesisDirectory,getNameetlengthpourobtenirlesinformationsvoulues.
importjava.io.*;//pourlaclasseFile
419
publicclassListRep
{publicstaticvoidmain(Stringargs[])
{StringnomRepert;
FileobjRep;
booleanok;
/*lecturenomderepertoire*/
ok=false;
do
{System.out.print("nomdurepertoire:");
nomRepert=Clavier.lireString();
objRep=newFile(nomRepert);
if(objRep.isDirectory())
ok=true;
else
System.out.println ("Nom incorrect (inexistant ou non
repertoire)");
}
while(!ok);
/*affichagedesinformationscorrespondantes*/
File[]membres=objRep.listFiles();
for(inti=0;i<membres.length;i++)
{Stringtype;
System.out.print(membres[i].getName()+"");
if(membres[i].isFile())
System.out.println ("FICHIER " + membres[i].length() + "
octets");
else
System.out.println("REPERTOIRE");
}
}
}
1.L’utilisateurpeutfournirindifféremmentunnomrelatif(aurépertoirecourant)ouunnomabsolu.2.Aulieudelaméthode listFiles,nousaurionspuaussiutiliser listqui fournituntableau de chaînes dans lequel chaque élément représente un nom de membre. Ilaurait alors fallu créer les objets de type File correspondants pour obtenir les
420
informationsvoulues.
421
Chapitre16
Laprogrammationgénérique
Connaissancesrequises
•Notiondeclassegénériqueetdeparamètredetype
•Définitionetutilisationd’uneclassegénérique
•Notiond’effacementduparamètredetypeetleslimitationsquiendécoulent(instanciationd’unobjetd’untypegénérique,tableauxd’objetsd’untypeparamétré,champsstatiquesd’untypeparamétré)
•Notiondeméthodegénérique
•Limitationdesparamètresdetyped’uneclassegénériqueoud’uneméthodegénérique
•Différentespossibilitésdedérivationd’uneclassegénérique
•Relationde«fauxhéritage»:siT’dérivedeT,C<T’>nedérivepasdeC<T>
•Notiondejokersimple
•Jokeraveccontraintes
Note:Laprogrammationgénériquen’estdisponiblequ’àpartirduJDK5.0.
422
152Classegénériqueàunparamètredetype
Écrire une classe génériqueTriplet permettant demanipuler des triplets d’objetsd’unmêmetype.Onladotera:
•d’unconstructeuràtroisarguments(lesobjetsconstituantletriplet),
• de troisméthodes d’accèsgetPremier,getSecond et getTroisieme, permettantd’obtenirlaréférencedel’undesélémentsdutriplet,
•d’uneméthodeafficheaffichantlavaleurdesélémentsdutriplet.Écrireunpetitprogrammeutilisantcetteclassegénériquepourinstancierquelquesobjetsetexploiterlesméthodesexistantes.
Ladéfinitiond’uneclassegénériquesefaitàl’aided’unsymbole(ici,T) représentantuntypeclassequelconquequel’onprécisedanslenomdelaclassecommedans:classTriplet<T>
OnutilisecesymboleTdanslasuitedeladéfinitiondelaclasse,commes’ils’agissaitd’untypedonné.VoicicommentnouspouvonsdéfinirlaclassegénériqueTriplet:classTriplet<T>
{privateTx,y,z;//lestroisélémentsdutriplet
publicTriplet(Tpremier,Tsecond,Ttroisieme)
{x=premier;y=second;z=troisieme;
}
publicTgetPremier()
{returnx;
}
publicTgetSecond()
{returny;
}
publicTgetTroisieme()
423
{returnz;
}
publicvoidaffiche()
{ System.out.println ("premiere valeur : " + x + " - deuxieme
valeur:"+y
+"-troisiemevaleur:"+z);
}
}
Notez que dans laméthodeaffiche nous nous fondons implicitement sur la méthodetoStringdesobjetsconcernés.VoiciunpetitprogrammeutilisantcetteclasseTriplet:
publicclassTstTriplet
{publicstaticvoidmain(Stringargs[])
{Integeroi1=3;//équivalentà:Integeroi1=newInteger
(3);
Integeroi2=5;//équivalentà:Integeroi2=newInteger
(5);
Integeroi3=12;//équivalentà:Integeroi3=newInteger
(12);
Triplet<Integer>ti=newTriplet<Integer>(oi1,oi2,oi3);
//onauraitaussipuécriredirectement:
//Triplet<Integer>ti=newTriplet<Integer>(3,5,12);
ti.affiche();
Triplet<Double>td=newTriplet<Double>(2.0,12.0,2.5);
//onpeutfournirdesargumentsdetypedoublequiseront
//convertisautomatiquementenDouble
td.affiche();
Integern=ti.getTroisieme();
System.out.println("troisiemeelementdutripletti="+n);
Doublep=td.getPremier();
System.out.println("premierelementdutriplettd="+p);
}
}
premierevaleur:3-deuxiemevaleur:5-troisiemevaleur:12
premierevaleur:2.0-deuxiemevaleur:12.0-troisiemevaleur:
2.5
troisiemeelementdutripletti=12
premierelementdutriplettd=2.0
424
153Classegénériqueàplusieursparamètresdetype
Écrire une classe générique TripletH semblable à celle de l’exercice précédent,mais permettant cette fois demanipuler des triplets d’objets pouvant être chacund’untypedifférent.Écrireunpetitprogrammeutilisantcetteclassegénériquepourinstancierquelquesobjetsetexploiterlesméthodesexistantes.
Dansladéfinitiondelaclasse,ilsuffitdeprévoircettefoistroisparamètresdetype.SinouslesnommonsT,UetV,ilsserontannoncésainsidanslenomdeclasse:
classTripletH<T,U,V>
VoicicequepourraitêtreladéfinitiondeTripletH:classTripletH<T,U,V>
{privateTx;privateUy;privateVz;//lestroiséléments
dutriplet
publicTripletH(Tpremier,Usecond,Vtroisieme)
{x=premier;y=second;z=troisieme;
}
publicTgetPremier()
{returnx;
}
publicUgetSecond()
{returny;
}
publicVgetTroisieme()
{returnz;
}
publicvoidaffiche()
{ System.out.println ("premiere valeur : " + x + " - deuxieme
valeur:"+y
+"-troisiemevaleur:"+z);
}
425
}
Etenvoiciunpetitprogrammed’utilisation:publicclassTstTripletH
{publicstaticvoidmain(Stringargs[])
{Integeroi=3;
Doubleod=5.25;
Stringos="hello";
TripletH<Integer,Double,String>tids
=newTripletH<Integer,Double,String>(oi,od,os);
tids.affiche();
Integern=tids.getPremier();
System.out.println("premierelementdutripletti="+n);
Doubled=tids.getSecond();
System.out.println("secondelementdutriplettd="+d);
}
}
premiere valeur : 3 - deuxieme valeur : 5.25 - troisieme valeur :
hello
premierelementdutripletti=3
secondelementdutriplettd=5.25
426
154Conséquencesdel’effacement(1)
Repérerleserreurscommisesdanslesinstructionssuivantes:classC<T>
{Tx;
T[]t1;
T[]t2;
publicstaticTinf;
publicstaticintcompte;
voidf()
{x=newT();
t2=t1;
t2=newT[5];
}
}
Rappelonsque,lorsdelacompilation,latechniquedite«del’effacement»,consisteàremplaceruntypegénériqueparun«typebrut».Enl’absenced’indicationscontraires(limitationsdesparamètresdetype),cetypebrutesttoutsimplementObject.Danscesconditions,uncertainnombred’opérationssontimpossibles,notamment:
•définitiond’unchampstatiqued’untypegénérique,
•instanciationd’untypegénériqueou,afortiori,d’untableaud’untypegénérique.classC<T>
{Tx;//OK
T[]t1;//OK
T[]t2;//OK
public static T inf ; // champ statique d'un type générique
interdit
publicstaticintcompte;
voidf()
427
{ x = new T () ; // instanciation d'un type générique
impossible
t2=t1;//OK
t2 = new T [5] ; // instanciation d'un tableau d'un type
générique
//impossible
}
}
428
155Conséquencesdel’effacement(2)
Quelsserontlesrésultatsfournisparceprogramme?publicclassTstStatic
{publicstaticvoidmain(Stringargs[])
{C<Integer>ci=newC<Integer>();
ci.affiche();
C<Double>cd=newC<Double>();
ci.affiche();cd.affiche();
Classcci=ci.getClass();
Classccd=cd.getClass();
if(cci==ccd)System.out.println
("cietcdsontdelamemeclasse");
else System.out.println ("ci et cd ne sont pas de la meme
classe");
System.out.println(cci.getName()+""+ccd.getName());
}
}
classC<T>
{publicC(){compte++;}
publicvoidaffiche()
{System.out.println("compte="+compte);
}
publicvoidaff()
{System.out.println("compte="+compte);
}
privatestaticintcompte=0;
}
Compte tenu de l’effacement, lors de l’exécution, il n’existe qu’une seule classecorrespondantautypebrutdeC<Integer>ouC<Double>,àsavoirsimplementC.Le
429
champ statique compte n’est finalement qu’un champ statique de cette classe C. Iln’existe donc qu’un seul « compteur » nommé compte pour tous les objets de typeC<T>,quellequesoitlavaleurdeT.Demême,laméthodegetClassappliquéeàcesdifférentsobjetsfournitlamêmevaleur,àsavoirlaréférenceàunobjetdetypeClassdontlenomestC.Voicifinalementlesrésultatsfournisparceprogramme:compte=1
compte=2
compte=2
cietcdsontdelamemeclasse
CC
430
156Méthodegénériqueàunargument
Écrireuneméthodegénériquefournissantenretourunobjet tiréauhasarddansuntableaufournienargument.Écrireunpetitprogrammeutilisantcetteméthode.
Ilsuffitderéaliseruneméthodegénériquepossédantunseulparamètredetype,ayantunentêtedelaformesuivante:static<T>Thasard(T[]valeurs)
Lechoixd’unélémentsefaitentirantsapositionauhasard,enrecourantàlaméthodeMath.random qui fournit une valeur au hasard dans l’intervalle [0, 1[. Voici ladéfinitiondenotreméthodeaccompagnéed’unpetitprogrammedetest:publicclassHasard
{static<T>Thasard(T[]valeurs)
{if(valeurs==null)returnnull;
intn=valeurs.length;
if(n==0)returnnull;
inti=(int)(n*Math.random());
returnvaleurs[i];
}
publicstaticvoidmain(Stringargs[])
{Integer[]tabi={1,7,8,4,9};//iciboxingautomatique
System.out.println("hasardsurtabi="+hasard(tabi));
String[]tabs={"Java","C","C++","C#","VisualBasic"};
System.out.println("hasardsurtabs="+hasard(tabs));
}
}
hasardsurtabi=4
hasardsurtabs=VisualBasic
431
157Méthodegénériqueeteffacement
Écrire une méthode qui renvoie au hasard un objet choisi parmi deux objets demêmetypefournisenargument.Écrireunpetitprogrammeutilisantcetteméthode.
Làencore,ilsuffitderéaliseruneméthodegénériqueàunseulparamètredetype,etàdeuxargumentsdecetype:publicstatic<T>Thasard(Tx,Ty)
{doublev=Math.random();
if(v<0.5)returnx;
elsereturny;
}
En revanche, cette fois, compte tenu de l’effacement, cette méthode sera compiléecommesionl’avaitécritedelafaçonsuivante:publicstaticObjecthasard(Objectx,Objecty)
{doublev=Math.random();
if(v<0.5)returnx;
elsereturny;
}
Ainsi,desappelsdehasardavecdesargumentsdetypesdifférentsserontacceptésparlecompilateur.Ilrestecependantpossibledeforcerlecompilateuràs’assurerquelesargumentseffectifssontd’unmêmetype,oud’untypecompatibleavecuntypedonné.Onlepréciselorsdel’appelàl’aided’unesyntaxedelaformesuivante,danslaquellenomClassecorrespondaunomdelaclasseoùlaméthodegénériqueestdéfinie:nomClasse<Type>.nomMéthode(arguments)
Nousenfournissonsquelquesexemplesencommentairesdupetitprogrammedetestdelaméthodehasard:publicclassMethGen2arg
{publicstaticvoidmain(Stringargs[])
{Integeri1=3;Integeri2=5;
432
System.out.println("hasard(i1,i2)="+hasard(i1,i2));
Strings1="Salut";Strings2="bonjour";
System.out.println("hasard(s1,s2)="+hasard(s1,s2));
System.out.println("hasard(i1,s1)="+hasard(i1,s1));
//Lesappelssuivantsserontrejetésencompilation:
//MethGen2arg.<Integer>hasard(i1,s1);
//MethGen2arg.<String>hasard(i1,s1);
//Enrevanche,ceux-ciserontacceptés:
//MethGen2arg.<Integer>hasard(i1,i2);
//MethGen2arg.<Number>hasard(i1,i2);
}
publicstatic<T>Thasard(Tx,Ty)
{doublev=Math.random();
if(v<0.5)returnx;
elsereturny;
}
}
433
158Dérivationdeclassesgénériques
Ondisposedelaclassegénériquesuivante:classCouple<T>
{privateTx,y;//lesdeuxélémentsducouple
publicCouple(Tpremier,Tsecond)
{x=premier;y=second;
}
publicvoidaffiche()
{System.out.println("premierevaleur:"+x
+"-deuxiemevaleur:"+y);
}
}
1. Créer, par dérivation, un classeCoupleNomme permettant demanipuler descouplesanaloguesàceuxdelaclasseCouple<T>,maispossédant,enoutre,unnomdetypeString.Onredéfiniraconvenablementlesméthodesdecettenouvelleclasseenréutilisantlesméthodesdelaclassedebase.2.ToujourspardérivationàpartirdeCouple<T>,créercettefoisune«classeordinaire»(c’est-à-direuneclassenongénérique),nomméePointNomme,danslaquellelesélémentsducouplesontdetypeIntegeret lenom,toujoursdetypeString.3.ÉcrireunpetitprogrammedetestutilisantcesdeuxclassesCoupleNommeetPointNomme.
1.Ilsuffitd’exploiterlespossibilitésdedérivationdeclassesgénériques,encréantune nouvelle classe possédant lemême paramètre de type que la classe de base.VoicicequepourraitêtreladéfinitiondenotreclasseCoupleNomme:classCoupleNomme<T>extendsCouple<T>
{privateStringnom;
publicCoupleNomme(Tpremier,Tsecond,Stringnom)
{super(premier,second);
this.nom=nom;
434
}
publicvoidaffiche()
{System.out.print("nom="+nom+"-");
super.affiche();
}
}
2.Cettefois,oncréeuneclassenongénérique,dérivantd’unclassegénérique,danslaquelleonfixeleparamètredetype(iciT=Integer).VoicicequepourraitêtreladéfintiondenotreclassePointNomme:classPointNommeextendsCouple<Integer>
{privateStringnom;
publicPointNomme(Integerpremier,Integersecond,Stringnom)
{super(premier,second);
this.nom=nom;
}
publicvoidaffiche()
{System.out.print("nom="+nom+"-");
super.affiche();
}
3. Voici un programme utilisant ces deux nouvelles classes, accompagné d’unexempled’exécution:publicclassTstDerivCouple
{publicstaticvoidmain(Stringargs[])
{Couple<Double>cd1=newCouple<Double>(5.0,2.5);
cd1.affiche();
Couple<Double>cd2=newCouple<Double>(5.0,2.5);
cd2.affiche();
CoupleNomme<String>cns
=newCoupleNomme<String>("hello","bonjour","saluts");
cns.affiche();
CoupleNomme<Couple<Double>>cnd
=newCoupleNomme<Couple<Double>>(cd1,cd2,"cf1");
cnd.affiche();
PointNommep1=newPointNomme(2,5,"Point1");
p1.affiche();
}
}
premierevaleur:5.0-deuxiemevaleur:2.5
435
premierevaleur:5.0-deuxiemevaleur:2.5
nom=saluts-premierevaleur:hello-deuxiemevaleur:bonjour
nom = cf1 - premiere valeur : Couple@923e30 - deuxieme valeur :
Couple@130c19b
nom=Point1-premierevaleur:2-deuxiemevaleur:5
Notez qu’ici, nous avons exploité les possibilités de « composition » dansl’instanciationde laclassegénériquecnd, encréantunobjetde typeCoupleNomme,danslequellesélémentssontd’untypeCouple<Double>.Onconstatequelaméthodeaffiche fournit alors simplement les adresses des deux éléments (de typeCouple<Double>) du couple. En effet, ici, cette méthode se contente d’utiliserimplicitementlaméthodetoStringdutypeconcerné(Couple<Double>).
436
159Lesdifférentessortesderelationd’héritage
Onsupposequ’onadéfiniuneclassegénériquenomméeC:classC<T>{.....}
ainsiqu’uneclasseordinairenomméeX.Pourchacunedesdéfinitionssuivantes,donnerlesrelationsd’héritageexistantentrelesclassesmentionnéesencommentaires:
classD<T>extendsC<T>{.....}/*définition1
*/
//C<Object>,C<Double>,D<Object>,D<Double>
classD<T,U>extendsC<T>{.....}/*définition2
*/
//C<Double>,D(Double,Integer),D(Double,Double),
//D(Integer,Double)
classD<TextendsNumber>extendsC<T>{.....}/*définition
3*/
//D<Double>,C<Double>,D<String>,C<String>
classD<T>extendsX{.....}/*définition4
*/
//D<Double>,X,D<String>
classD<T>extendsC<String>/*définition5
*/
//D<String>,D<Integer>,C<String>,C<Integer>
1.D<Double>dérivedeC<Double>D<Object>dérivedeC<Object>Enrevanche, iln’existeaucunerelationd’héritageentreD<Double>etD<Object>,pasplusqu’entreC<Double>etC<Object>.2.D<Double,Integer>dérivedeC<Double>D<Double,Double>dérivedeC<Double>Enrevanche,D<Integer,Double>etC<Double> ne sont pas liés par une relation
437
d’héritage.3. D<Double> dérive de C<Double> car Double implémente bien l’interfaceNumber. En revanche, D<String> ne dérive pas de C<String> puisque Stringn’implémentepasNumber.4.D<Double>dérivedeXD<String>dérivedeX5.D<String>dérivedeC<String>D<Integer>dériveeC<String>Enrevanche,D<Integer>nepossèdeaucunliend’héritageavecC<Integer>.
438
160Limitationsdesparamètresdetyped’uneméthode
Ecrire uneméthode générique déterminant le plus grand élément d’un tableau, lacomparaisondesélémentsutilisant l’ordre induitpar laméthodecompareTo de laclassedesélémentsdutableau.
Onpourraitenvisagerpournotreméthode,nomméemax,unen-têtedecetteforme:static<T>Tmax(T[]valeurs)
Mais,danscecas,lecompilateurrefuseraitl’applicationdelaméthodecompareToàdesélémentsdetypeT.Pourquecesoitpossible,ilestnécessairedepréciserqueletypeTimplémentel’interfaceComparable<T>,enemployantunen-têtedecetteformestatic<TextendsComparable<T>>Tmax(T[]valeurs)
Voiciladéfinitiondelaméthodeetunexempled’utilisation:publicclassMaxTab
{publicstaticvoidmain(Stringargs[])
{Integer[]td={2,8,1,7,4,9};
System.out.println("maxidetd="+max(td));
String[]ts={"bonjour","hello","salut"};
System.out.println("maxidets="+max(ts));
}
static<TextendsComparable<T>>Tmax(T[]valeurs)
{if(valeurs==null)returnnull;
if(valeurs.length==0)returnnull;
Tmaxi=valeurs[0];
for(Tv:valeurs)if(v.compareTo(maxi)>0)maxi=v;
returnmaxi;
}
}
maxidetd=9
maxidets=VisualBasic
439
En toute rigueur, dans certains cas, la spécificationComparable<T> de l’en-tête demaxpourraposerdesproblèmesetilfaudrarecouriràdesjokersdetypesuper,enlaremplaçantpar<TextendsComparable<?superT>>,à l’instardecequisefaitdanscertainesméthodesrelativesauxcollections.Cepoint,dontlajustificationsortducadre de ce manuel, concerne essentiellement les développeurs de bibliothèquesgénériques.
440
161RedéfinitiondelaméthodecompareTo
Compléter la classePoint suivante, demanière à ce que l’onpuisse appliquer laméthode générique max précédente à un tableau d’objets de type Point. Onconviendraquelespointssontordonnésparleurdistanceàl’origine.
classPoint
{privateintx,y;
Point(intx,inty)
{this.x=x;this.y=y;
}
publicvoidaffiche()
{System.out.println("coordonnees:"+x+""+y);
}
}
Il faut faire implémenter à la classe Point, l’interface Comparable <Point> dontl’uniqueméthodeapouren-tête:publicintcompareTo(Pointp)
D’où lanouvelledéfinitiondenotreclassePoint (nepasoublierdementionnerque,dorénavant,laclassePointimplémenteComparable<Point>:classPointimplementsComparable<Point>
{privateintx,y;
Point(intx,inty)
{this.x=x;this.y=y;
}
publicvoidaffiche()
{System.out.println("coordonnees:"+x+""+y);
}
publicintcompareTo(Pointp)
{intnorme1=x*x+y*y;
441
intnorme2=p.x*p.x+p.y*p.y;
if(norme1==norme2)return0;
if(norme1>norme2)return1;
elsereturn-1;
}
Voici un petit programme appliquant la méthodemax à des objets du nouveau typePoint(parsoucidelisibilité,nousavonsreproduitlalistedelaméthodemax):publicclassMaxTabPoints
{publicstaticvoidmain(Stringargs[])
{Pointp1=newPoint(0,5);
Pointp2=newPoint(3,1);
Pointp3=newPoint(0,12);
Pointp4=newPoint(3,5);
Point[]tp={p1,p2,p3,p4};
Pointmaxp=max(tp);
System.out.println("Pointmaxi:");
maxp.affiche();
}
static<TextendsComparable<T>>Tmax(T[]valeurs)
{if(valeurs==null)returnnull;
if(valeurs.length==0)returnnull;
Tmaxi=valeurs[0];
for(Tv:valeurs)if(v.compareTo(maxi)>0)maxi=v;
returnmaxi;
}
}
pointmaxi:
coordonnees:012
442
Chapitre17
Lescollectionsetlestablesassociatives
Connaissancesrequises
•Principalesméthodesdel’interfaceCollection,indépendantesd’unitérateur:add,size,contains,addAll,retainAlletremoveAll
•InterfaceIteratoretméthodesnext,hasNextetremove
•InterfaceListIteratoretméthodesprevious,hasPrevious,setetadd
•Ordredesélémentsd’unecollection;interfaceComparableetméthodecompareTo;objetscomparateurs
•Relativitédelanotiond’égalitédedeuxélémentsd’unemêmecollection;rôledelaméthodeequals
•Utilisationdelabouclefor…eachsurdescollections
•ClasseLinkedListetméthodesspécifiquesremoveFirstetremoveLast
•ClasseArrayListetméthodesspécifiquestravaillantavecunepositiondonnée:get,set,addetremove
•ClasseHashSetetméthodeshashCodeetequals
•ClasseTreeSetetméthodecompareTo
Note:noussupposonsquenoustravaillonsavecuneversionJava5oupostérieure,cequinouspermetd’utiliserdes collections et des tablesgénériques, l’emballage et ledéballageautomatique,ainsiquelaboucleditefor…each.Enrevanche,nousneferonspas appel aux spécificités du JDK 8, lesquelles ne seront exploitées que dans lechapitresuivant.
443
• Algorithmes applicables aux collections : tri, recherche de maximum ou deminimum,recherchebinaire,copie;rôledelaméthodecompareTooud’unobjetcomparateur
• Tables associatives, interfaceMap, classesHashMap et TreeMap, méthodesput,getetremove
•Notiondevue associée àune table : classeMap.Entry etméthodes entrySet,keySet,getKeyetgetValue
444
162Dépendanceouindépendanced’unitérateur
Quelsrésultatsfourniraceprogramme:importjava.util.*;
publicclassEssai
{publicstaticvoidmain(Stringargs[])
{LinkedList<Integer>liste=newLinkedList<Integer>();
liste.add(3);liste.add(5);nliste.add(3);
liste.add(12);nliste.add(3);
System.out.println(liste);
liste.remove(3);System.out.println(liste);
liste.remove(newInteger(3));System.out.println(liste);
Iterator<Integer>it=liste.iterator();
while(it.hasNext())if(it.next()==3)it.remove();
System.out.println(liste);
}
}
Rappelons tout d’abord que, depuis Java 5, les possibilités dites d’emballage et dedéballageautomatiques(autoboxing)permettent le recoursautomatiqueàdesclassesenveloppes.Ainsi,l’appel:
liste.add(3);
remplaceavantageusement:liste.add(newInteger(3));
Cependant,cettedémarchenes’appliqueplusdansl’appel:liste.remove(3);
Eneffet, ilexisteuneméthoderemove(int)qui,comptetenudesrèglesrelativesàlasurdéfinition,setrouveraappeléeici.Ellesupprimel’élémentderang3deliste.Enrevanche:
liste.remove(newInteger(3));
445
supprimebienlepremierélémentdelalistedontlavaleurestégaleà3.Rappelonsquel’égalitésefondesurlaméthodeequalslaquelle,danslecasdesclassesenveloppes,considèrebienlavaleurdesobjets.Enfin,laboucle:
while(it.hasNext())if(it.next()==3)it.remove();
permetdesupprimertousleséléments(restants)dontlavaleurestégaleà3.Rappelonsque la méthode remove supprime l’élément courant (c’est-à-dire celui désigné parl’itérateur).Enfin,dansuneinstructiontelleque:
System.out.println(liste);
il y a appel de la méthode toString de l’objet liste. Celle-ci, comme pour toutecollection,appellelaméthodetoStringdechacundeseséléments.Endéfinitive,ceprogrammefournitlesrésultatssuivants:
[3,5,3,12,3]
[3,5,3,3]
[5,3,3]
[5]
446
163Manipulationd’untableaudetypeArrayList
Ondisposed’unobjettabdéclaréainsi:ArrayList<Integer>tab;
Écrirelesinstructionsréalisantlesactionssuivantessurlesvaleursdetab:
•affichagedansl’ordrenaturel(onproposeraaumoins4solutions);
•affichagedansl’ordreinverse(aumoins2solutions);
•affichagedesélémentsderangpair(0,2,4…)(aumoins2solutions);
•miseàzérodesélémentsdevaleurnégative(aumoins2solutions).
L’affichagedansl’ordrenaturelpeutsefaire:
•enutilisantlaboucleditefor…each:for(intelem:tab)System.out.print(elem+"");
•enutilisantlerecoursautomatiqueàlaméthodetoStringdeArrayList:System.out.println(tab);
•enrecourantàlaméthodegetpourparcourirlesdifférentsélémentsdutableau:for(inti=0;i<tab.size();i++)System.out.print(tab.get(i)+"
");
•enutilisantunitérateur:ListIterator<Integer>it=tab.listIterator();
while(it.hasNext())System.out.print(it.next()+"");
L’affichage dans l’ordre inverse ne peut plus utiliser les deux premières démarches.Lesdeuxdernièresrestentapplicables:
for(inti=tab.size()-1;i>=0;i--)System.out.print(tab.get(i)+
"");
ListIterator<Integer>itr=tab.listIterator(tab.size());//fin
deliste
while(itr.hasPrevious())System.out.print(itr.previous()+"");
447
Ilenvademêmepourl’affichagedesélémentsderangpair:for(inti=0;i<tab.size();i+=2)System.out.print(tab.get(i)+"
");
System.out.println("\nelementsderangpair-methode2");
while(itp.hasNext())
{System.out.print(itp.next()+"");
itp.next();
}
Lamiseàzérodesélémentsnégatifsnepeut,làencore,sefairequ’enutilisantsoitlesméthodesgetetset,soitunitérateur:
for(inti=0;i<tab.size();i++)if(tab.get(i)<0)tab.set(i,
0);
ListIterator<Integer>itz=tab.listIterator();
while(itz.hasNext())if(itz.next()<0)itz.set(0);
Notez bien que la boucle for… each ne permet que des consultations des élémentsd’une collection. Elle n’est donc pas utilisable ici. Bien que correcte sur un plansyntaxique, l’instruction suivante se contenterait d’agir à plusieurs reprises sur lavaleurdeelem(lamettantàzérolorsqu’elleestnégative),maislaisseraitinchangéelavaleurcorrespondantedutableau:for(intelem:tab)if(elem<0)elem=0;
Voici un exemple de programme complet reprenant ces diverses démarches,accompagnéd’unexempled’exécution.Notezque,pourtesterlesdémarchesdemiseàzérodesélémentsnégatifs,nousavonsdûtravaillersurunecopiedutableauinitial.importjava.util.*;
publicclassTableau
{publicstaticvoidmain(Stringargs[])
{intt[]={3,-5,9,2,0,-8,12,7,3,12};
ArrayList<Integer>tab=newArrayList<Integer>();
for(intelem:t)tab.add(elem);
//affichageordrenaturel
System.out.println("ordrenaturel-methode1");
for(intelem:tab)System.out.print(elem+"");
System.out.println("\nordrenaturel-methode2");
System.out.println(tab);
System.out.println("ordrenaturel-methode3");
for(inti=0;i<tab.size();i++)System.out.print(tab.get(i)+"
");
448
System.out.println("\nordrenaturel-methode4");
ListIterator<Integer>it=tab.listIterator();
while(it.hasNext())System.out.print(it.next()+"");
//affichageordreinverse
System.out.println("\nordreinverse-methode1");
for (int i=tab.size()-1 ; i>=0 ; i--) System.out.print
(tab.get(i)+"");
System.out.println("\nordreinverse-methode2");
ListIterator<Integer>itr=tab.listIterator(tab.size());//fin
deliste
while(itr.hasPrevious())System.out.print(itr.previous()+"");
//affichageélémentsderangpair
System.out.println("\nelementsderangpair-methode1");
for(inti=0;i<tab.size();i+=2)System.out.print(tab.get(i)+
"");
System.out.println("\nelementsderangpair-methode2");
ListIterator<Integer>itp=tab.listIterator();
while(itp.hasNext())
{System.out.print(itp.next()+"");
itp.next();
}
//miseazerod'unecopiedetab
ArrayList<Integer>tab1=newArrayList<Integer>(tab);
System.out.println("\nmiseazero-methode1");
for(inti=0;i<tab1.size();i++)if(tab1.get(i)<0)tab1.set
(i,0);
System.out.println(tab1);
tab1=newArrayList<Integer>(tab);
System.out.println("miseazero-methode2");
ListIterator<Integer>itz=tab1.listIterator();
while(itz.hasNext())if(itz.next()<0)itz.set(0);
System.out.println(tab1);
}
}
ordrenaturel-methode1
3-5920-8127312
ordrenaturel-methode2
[3,-5,9,2,0,-8,12,7,3,12]
449
ordrenaturel-methode3
3-5920-8127312
ordrenaturel-methode4
3-5920-8127312
ordreinverse-methode1
123712-8029-53
ordreinverse-methode2
123712-8029-53
elementsderangpair-methode1
390123
elementsderangpair-methode2
390123
miseazero-methode1
[3,0,9,2,0,0,12,7,3,12]
miseazero-methode2
[3,0,9,2,0,0,12,7,3,12]
450
164Trid’unecollection(1)
OndisposedelaclasseCerclesuivante:classCercle
{publicCercle(intx,inty,doublerayon)
{this.x=x;this.y=y;this.rayon=rayon;}
publicvoidaffiche()
{System.out.println("Coordonnees:"+x+","+y
+";rayon:"+rayon);
}
publicdoublegetRayon(){returnrayon;}
publicintgetX(){returnx;}
privateintx,y;
doublerayon;
}
Écrirelesinstructionspermettantdetrier,sansmodifierlaclasseCercle,untableau(detypeArrayList)d’objetsdetypeCercle:
•suivantlesvaleurscroissantesdeleurrayon;
•suivantlesvaleurscroissantesdeleurabscisse.
LaclasseCollectionsfournitdifférentsalgorithmesdetrid’unecollectionquelconqueimplémentantl’interfaceList,cequiestlecasdeArrayList.L’ordredetriyestdéfinisoit par la méthode compareTo de la classe concernée qui doit alors implémenterl’interfaceComparable, soit par ce que l’on nommeun objet comparateur, fourni enargumentdel’algorithmedetri.Manifestementici,laclasseCerclen’implémentantpasl’interfaceComparator,ilfautse tournervers lasecondedémarche. Ici,notreobjetcomparateurdevra implémenterl’interface Comparator<Cercle>, c’est-à-dire disposer d’une méthode compare(Cercle,Cercle)renvoyantunentier(dontlavaleurexacteestsansimportance):
•négatifsilepremierargumentestconsidérécommeinférieurausecond;
•nulsilepremierargumentestconsidérécommeégalausecond;
451
•positifsilepremierargumentestconsidérécommesupérieurausecond.Voici ce que pourrait être le code demandé. Ici, nous avons choisi (arbitrairement)d’utiliser une classe comparateur pour le premier tri et une classe anonyme pour lesecond.
importjava.util.*;
publicclassEssaiComparateur
{publicstaticvoidmain(Stringargs[])
{ArrayList<Cercle>liste=newArrayList<Cercle>();
Cerclec1=newCercle(5,3,5.0);
Cerclec2=newCercle(1,9,3.5);
Cerclec3=newCercle(2,9,2.5);
liste.add(c1);liste.add(c2);liste.add(c3);
//trisuivantlerayonducercle
Collections.sort(liste,newComparateur1());
System.out.println("--Cerclestriesparrayoncroissant");
for(Cerclec:liste)c.affiche();
//trisuivantl'abcisseducercle
Collections.sort(liste,newComparator<Cercle>()
{publicintcompare(Cerclec1,Cerclec2)
{doublex1=c1.getX();doublex2=c2.getX();
if(x1<x2)return-1;
elseif(x1==x2)return0;
elsereturn1;
}
});
System.out.println("--Cerclestriesparabscissecroissante");
for(Cerclec:liste)c.affiche();
}
}
classComparateur1implementsComparator<Cercle>
{publicintcompare(Cerclec1,Cerclec2)
{doubler1=c1.getRayon();
doubler2=c2.getRayon();
if(r1<r2)return-1;
elseif(r1==r2)return0;
elsereturn1;
}
}
452
classCercle
{publicCercle(intx,inty,doublerayon)
{this.x=x;this.y=y;this.rayon=rayon;}
publicvoidaffiche()
{System.out.println("Coordonnees:"+x+","+y
+";rayon:"+rayon);
}
publicdoublegetRayon(){returnrayon;}
publicintgetX(){returnx;}
privateintx,y;
doublerayon;
}
--Cerclestriesparrayoncroissant
Coordonnees:2,9;rayon:2.5
Coordonnees:1,9;rayon:3.5
Coordonnees:5,3;rayon:5.0
--Cerclestriesparabscissecroissante
Coordonnees:1,9;rayon:3.5
Coordonnees:2,9;rayon:2.5
Coordonnees:5,3;rayon:5.0
Notez que l’écriture de la classe anonyme dans l’appel deCollections.sort pourraitégalementutiliserlaméthodecompareTodelaclasseInteger,moyennantl’emploideconversionsdeintenInteger:Collections.sort(liste,newComparator<Cercle>()
{publicintcompare(Cerclec1,Cerclec2)
{ return ((Integer)(c1.getX())).compareTo((Integer)
(c2.getX()));
}
});
IlenvademêmepourlaméthodecomparedelaclasseComparateur1:publicintcompare(Cerclec1,Cerclec2)
{ return ((Double)(c1.getRayon())).compareTo((Double)
(c2.getRayon()));
}
453
165Trid’unecollection(2)
Modifier la classeCercle de l’exercice précédent, de manière à ce que l’appel(listeétantunobjetdetypeArrayList<Cercle>):
Collections.sort(liste);
trielesélémentsdeliste,suivantlesvaleurscroissantesdeleurrayon.
Cette fois, l’énoncé nous interdit d’employer un objet comparateur, comme nousl’avonsfaitdansl’exerciceprécédent.IlfautdoncobligatoirementquelaclaseCercleimplémente l’interfaceComparable et qu’elle définisse la méthode compareTo, defaçonappropriée.Voici laclasseCerclemodifiéedansce sens, accompagnéed’unpetit programmedetestetd’unexempled’exécution.importjava.util.*;
publicclassEssaiTri
{publicstaticvoidmain(Stringargs[])
{ArrayList<Cercle>liste=newArrayList<Cercle>();
Cerclec1=newCercle(5,3,5.0);
Cerclec2=newCercle(1,9,3.5);
Cerclec3=newCercle(2,9,2.5);
liste.add(c1);liste.add(c2);liste.add(c3);
//trisuivantlerayonducercle
Collections.sort(liste);
System.out.println("--Cerclestriesparrayoncroissant");
for(Cerclec:liste)c.affiche();
}
}
classCercleimplementsComparable<Cercle>
{publicCercle(intx,inty,doublerayon)
{this.x=x;this.y=y;this.rayon=rayon;}
publicvoidaffiche()
{System.out.println("Coordonnees:"+x+","+y
+";rayon:"+rayon);
454
}
publicintcompareTo(Cerclec)
{if(rayon<c.rayon)return-1;
elseif(rayon==c.rayon)return0;
elsereturn1;
}
//onpeutaussiutilisercompareTosurdesDouble:
//return((Double)(rayon)).compareTo((Double)(c.rayon))
publicdoublegetRayon(){returnrayon;}//inutiliseeici
publicintgetX(){returnx;}//inutiliseeici
privateintx,y;
doublerayon;
}
--Cerclestriesparrayoncroissant
Coordonnees:2,9;rayon:2.5
Coordonnees:1,9;rayon:3.5
Coordonnees:5,3;rayon:5.0
Notezque,cettefois,lesméthodesgetXetgetRayondelaclasseCerclenesontplusutilisées,puisquenotreméthodecompareToabienaccèsauxchampsprivés.Parailleurs,l’ordredetriestdéfini,unefoispourtoutes,danslaclasseCercleelle-même.Sil’onsouhaiteeffectuerd’autressortesdetris,ilfaudraquandmêmerecouriràladémarchedel’exerciceprécédentenfournissantunobjetcomparateur,indépendantdelaclasseCercle.
455
166Réalisationd’unelistetriéeenpermanence
RéaliseruneclassenomméeListeTrieepermettantdemanipulerunelistedechaînesde caractères, en s’arrangeant pour qu’elle soit triée en permanence. Outre leconstructeur,onladoteradesméthodes:
•ajoutequiajouteunnouvelélémentàlabonneplace;
•affichequiaffichelesélémentsdelaliste.
L’énoncé n’impose pas le type de collection à utiliser pour conserver les chaînes.L’interfaceListnousconvient toutàfaitpuisqu’ellepermetdeparcourir lesélémentsde la collection et d’insérer un nouvel élément entre deux autres. Nous pouvonsindifféremmentemployerunobjetdetypeArrayListoudetypeLinkedList.La méthode ajoute (String ch) devra rechercher dans la liste le premier élémentsupérieuràch.Siuntelélémentexiste,onajouterachavant(ilfaudrautiliserpreviouspour "reculer" l’itérateur).Si un tel élément n’existe pas, il suffira d’ajouterch à lapositioncourantedel’itérateur,etceciquelalistesoitvideounon.Voicicequepourraitêtre laclassedemandée, implémentée iciavecunobjetde typeLinkedList,accompagnéed’unpetitprogrammedetestetd’unexempled’exécution.
importjava.util.*;
publicclassTestListeTriee
{publicstaticvoidmain(Stringargs[])
{ListeTrieeliste=newListeTriee();
liste.ajoute("c");
liste.affiche();
liste.ajoute("b");
liste.affiche();
liste.ajoute("f");
liste.affiche();
liste.ajoute("e");
456
liste.affiche();
}
}
classListeTriee
{publicListeTriee()
{liste=newLinkedList<String>();//ouArrayList
}
publicvoidajoute(Stringch)
{ListIterator<String>it=liste.listIterator();
booleantrouve=false;
while((it.hasNext())&&!trouve)
{if(it.next().compareTo(ch)>0)trouve=true;
}
if (trouve) it.previous() ; // ici, il y obligatoirement un
precedent
it.add(ch);
}
publicvoidaffiche()
{
for(Stringch:liste)System.out.print(ch+"");
System.out.println();
}
privateLinkedList<String>liste;//ouArrayList
}
c
bc
bcf
bcef
457
167Créationd’unindex
Réaliseruneclassenommée Indexpermettantdegérerun indexd’ouvrage.Un telindexassocieuneentrée(motousuitedemots)àunouplusieursnumérosdepage(contrairementàcequi sepassedans les indexde laplupartdesouvrages,onneprévoirapasd’entréesàplusieursniveaux).LaclasseIndexdisposera,enplusd’unconstructeur,desméthodes:
•ajouterpourintroduireunenouvelleentrée,associéeàunnumérodepage;
•listepourafficherlalistedel’index,parordrealphabétiquedesentrées,lalistedes numéros de page d’une même entrée étant triée par valeur croissante ;l’affichaged’uneentréed’indexseprésenterasurunemêmelignesouslaforme:Java:1225
Onprendrabiensoinden’associerqu’uneseulefoisunmêmenumérodepageàuneentréedonnée.
Pour représenter notre index, nous utiliserons une table associative.Rappelons qu’ils’agit d’unensembledepaires, formant chacuneuneassociation entreuneclé et unevaleur. Ici, la clé sera une entrée d’index (de type String). Quant à la valeur, ellecorrespondra à la liste des numéros associés à une entrée. Ceux-ci pourraient êtreconservésdansunobjetdetypeList,maiscedernierdevraitalorsêtretriéaumomentdel’affichagedel’indexetdeplus,ilfaudraityéviterlesdoublons.Ilestplussimpled’utiliserunensemble(cequiéliminelesdoublons)detypeTreeSet(ilseraalorstriéenpermanence).Quantàlatableassociativeelle-même,nouschoisironségalementletypeTreeMap,desortequ’elleseratoujourstriéesurlesentréesd’index.Rappelonsque,dansunetableassociative(quelquesoitsontypeexact),lescléssonttoujoursuniques.Endéfinitive,notreindexseraconservédansunobjetdutype:TreeMap<String,TreeSet<Integer>>
Pourajouterunenouvelleentréeàl’index,laméthodeajouter(Stringentree,intnumero)
recherchera tout d’abord l’ensemble des numéros déjà associés à la clé entree, en
458
recourant à la méthode get (entree). Si cette entrée n’existe pas (la méthode getfourniraalorslavaleurnull),onintroduiraunenouvellepairedansl’index,àl’aidedelaméthodeput, à laquelleon fournira commecléentree et commevaleur un nouvelensembleforméduseulnumero.Dans lecascontraire (get fourniraalorsunevaleurnon nulle correspondant à la référence sur la valeur associée à la clé fournie), onajoutera numero à l’ensemble des numéros existants et l’on utilisera également laméthodeputpourintroduirelapairevouluedansl’index;commelescléssontuniques,lapaireainsiajoutéeprendrabienlaplacedel’ancienne.Unetableassociativenedisposepasd’itérateur.Poureffectuerlalistedenotreindex,nous devrons utiliser la méthode entrySet qui permet de "voir" la table comme unensembledepaires(clé,valeur).ChaquepaireestunélémentdetypeMap.EntrydontlesméthodesgetKeyetgetValuepermettentd’obtenirrespectivementlacléetlavaleurcorrespondante.Endéfinitive,voicinotreclasseIndex,accompagnéed’unpetitprogrammedetest:
importjava.util.*;
publicclassTestIndex
{publicstaticvoidmain(Stringargs[])
{IndexmonIndex=newIndex();
monIndex.ajouter("Java",25);
monIndex.ajouter("C++",45);
monIndex.ajouter("Java",12);
monIndex.ajouter("objet",15);
monIndex.ajouter("polymorphisme",45);
monIndex.liste();
}
}
classIndex
{publicIndex()
{index=newTreeMap<String,TreeSet<Integer>>();}
publicvoidajouter(Stringentree,intnumero)
{//sientreen'existepasdansl'index,onl'ajoute,associeau
numero
//sinon,onajoutelenumerodepageal'ensembledesnumeros
//dejaassociesaentree
TreeSet<Integer>numerosExistants=index.get(entree);
if(numerosExistants==null)
{TreeSet<Integer>numeroNouveauNom=newTreeSet<Integer>();
numeroNouveauNom.add(numero);
index.put(entree,numeroNouveauNom);
459
}
else
{numerosExistants.add(numero);
index.put(entree, numerosExistants) ; // remplace l'entree
precedente
}
}
publicvoidliste()
{Set<Map.Entry<String,TreeSet<Integer>>>
elementsIndex=index.entrySet();
for (Map.Entry <String, TreeSet <Integer> > elementCourant :
elementsIndex)
{StringentreeCourante=elementCourant.getKey();
TreeSet<Integer>numeros=elementCourant.getValue();
System.out.print(entreeCourante+":");
for(intnum:numeros)System.out.print(num+"");
System.out.println();
}
}
privateTreeMap<String,TreeSet<Integer>>index;
}
C++:45
Java:1225
objet:15
polymorphisme:45
460
168Inversiond’unindex
Modifier la classe Index précédente, de sorte qu’elle dispose de deuxméthodessupplémentaires:
• creationIndexPage créant un "index inversé", associant un numéro de pagedonnéàlalistedesentréescorrespondantes;onselimiteraauxnumérosdepageassociésàaumoinsuneentrée;
• listeIndexPage affichant la liste de cet index inversé, par ordre croissant desnumérosdepage,lesentréesd’unemêmepageétanttriéesparordrealphabétique,souslaforme:25:Javalangage
Nousdotonsnotreclassed’unnouveauchampindexPagedestinéàreprésenterl’indexinversé. Nous utiliserons également une table associative ; cette fois, la clécorrespondraàunnumérodepage(typeInteger)tandisquelavaleurcorrespondraàlalistedesentréesassociéesàlapagecorrespondante.Làencore,nouspourrionsutiliserunobjetdetypeList,maisunTreeSet<String>permettrad’éliminer lesdoublonsetdeconserverlalistedesentréesd’unepagetriéeparordrealphabétique.Unenouvellefois,pour la tableassociative,nouschoisirons le typeTreeMap,cequi luipermettrad’être triée automatiquement sur le numéro de page. En définitive, notre champindexPageseradéclaréainsi:privateTreeMap<Integer,TreeSet<String>>indexPage;
LaméthodecreationIndexPagedevraparcourirchacunedespairesde l’indexinitial.Pourcefaire,onutiliseralaméthodeentrySetquiserviraà"voir"notreindexcommeunensembledepaires(clé,valeur),chaquepaireétantunélémentdetypeMap.Entrydont les méthodes getKey et getValue permettant d’obtenir respectivement la clé(chaîne)etlavaleurassociée(ensembledenumérosdepage).Parallèlement,oncréeral’indexinversé,enprocédantdefaçonsimilaireàcequenousavions fait dans laméthodeajouter de l’exercice précédent.Cette fois, pour chaquenuméro de page n associé à une entrée e, on recherchera dans l’index inversé unélémentdeclén.S’il existe,onajouteraà sa listed’entrées l’entréee ; dans le cascontraire,oncréeraunnouvelélémentdeclén,dontlavaleurseraunensembleformé
461
delaseuleentréee.En définitive, voici notre nouvelle classe Index (nous n’avons pas reproduit lesméthodesajouteretliste),accompagnéed’unpetitprogrammedetest.importjava.util.*;
publicclassTestIndexParPage
{publicstaticvoidmain(Stringargs[])
{IndexmonIndex=newIndex();monIndex.ajouter("Java",25);
monIndex.ajouter("C++",45);monIndex.ajouter("Java",12);
monIndex.ajouter ("objet", 15) ; monIndex.ajouter
("polymorphisme",45);
monIndex.ajouter ("objet", 45) ; monIndex.ajouter ("langage",
25);
monIndex.creationIndexPage();
monIndex.listeIndexPage();
}
}
classIndex
{publicIndex(){//commeprecedemment}
public void ajouter (String entree, int numero) { // comme
precedemment}
publicvoidliste(){//commeprecedemment}
publicvoidcreationIndexPage()
{indexPage=newTreeMap<Integer,TreeSet<String>>();
Set<Map.Entry<String,TreeSet<Integer>>>
elementsIndex=index.entrySet();
for (Map.Entry <String, TreeSet <Integer> > elementCourant :
elementsIndex)
{StringentreeCourante=elementCourant.getKey();
TreeSet<Integer>pagesCourantes=elementCourant.getValue();
for(Integernumero:pagesCourantes)
{TreeSet<String>entreesExistantes=indexPage.get(numero);
if(entreesExistantes==null)
{ TreeSet <String> entreeNouveauNumero = new TreeSet <String>
();
entreeNouveauNumero.add(entreeCourante);
indexPage.put(numero,entreeNouveauNumero);
}
else
{entreesExistantes.add(entreeCourante);
indexPage.put(numero,entreesExistantes);
462
}
}
}
}
publicvoidlisteIndexPage()
{if(indexPage==null)return;
Set<Map.Entry<Integer,TreeSet<String>>>
elementsIndexPage=indexPage.entrySet();
for (Map.Entry <Integer, TreeSet <String> > numero :
elementsIndexPage)
{IntegernumeroCourant=numero.getKey();
TreeSet<String>entrees=numero.getValue();
System.out.print(numeroCourant+":");
for(Stringentree:entrees)System.out.print(entree+"");
System.out.println();
}
}
privateTreeMap<String,TreeSet<Integer>>index;
privateTreeMap<Integer,TreeSet<String>>indexPage;
}
12:Java
15:objet
25:Javalangage
45:C++objetpolymorphisme
463
Chapitre18
Lesexpressionslambdaetlesstreams
Connaissancesrequises
•Syntaxedesexpressionslambda
•Interfacefonctionnelle;lesprincipalesinterfacesfonctionnellesstandards
•Référencesdeméthodes
•L’interfaceComparator;méthodescomparing,reversed,reversedOrder
•Streamséquentieletstreamparallèle
•Lesdifférentessourcespourunstream:collection,listeoutableaudevaleurs,génération
•Lesméthodesintermédiaires:filter,map,sorted,limit,peek
•Lesméthodesterminales:forEach,forEachOrdered,count,sum,min,max,average
•Laméthodereduce(formeusuelle);laméthodecollectetlesobjetsCollectors:toList,toMap,groupingByetjoining
464
169Lambdaetinterfacesprédéfinies
Écrire la méthode affichage_selectif afin que le programme suivant affiche lesélémentspositifsdutableautab:publicclassAffichage
{publicstaticvoidmain(String[]args)
{int[]tab={1,4,-2,9,-3,5,-3};
System.out.print("--Lespositifsdetab:");
affichage_selectif(tab,ee->ee>0);
}
}
Onproposeradeuxsolutions,l’uneutilisantuneinterfacepersonnalisée,l’autreuneinterfaceprédéfinie.
On voit que la méthode affichage_selectif doit recevoir en second argument uneimplémentation d’une interface fonctionnelle dont laméthode fonctionnelle reçoit unargument de type int et fournit un résultat de type booléen. Avec une interfacepersonnalisée,ilpourraits’agirde(lesnomsFiltrageetfiltreétantarbitraires):
interfaceFiltrage
{publicBooleanfiltre(intn);
}
Voicicequepourraitêtrealorslaméthodeaffiche_selectif:publicstaticvoidaffichage_selectif(int[]t,Filtragef)
{for(intval:t)if(f.filtre(val))System.out.print(val+"
*");
System.out.println();
}
Onpeutsepasserdedéfinir l’interfaceFiltrageenrecourantà l’interfaceprédéfinieIntPredicate et à sa méthode fonctionnelle test (qui reçoit un int et renvoie unbooléen).Voicicequedeviendraitnotreprogrammecompletdanscecas:
importjava.util.function.*;
465
publicclassAffichage
{publicstaticvoidmain(String[]args)
{int[]tab={1,4,-2,9,-3,5,-3};
System.out.print("--Lespositifsdetab:");
affichage_selectif(tab,ee->ee>0);
}
publicstaticvoidaffichage_selectif(int[]t,IntPredicatef)
{for(intval:t)if(f.test(val))System.out.print(val+"*
");
System.out.println();
}
}
--Lespositifsdetab:1*4*9*5*
466
170Lambdaetréférences
Compléter la dernière solution de l’exercice précédent, de manière que leprogramme affiche, en plus des nombres positifs du tableau tab, les nombresnégatifs,puislesnombrespairs:
--Lespositifs:
1*4*9*5*12*7*6*
--Lesnegatifs:
-2*-3*-3*-11*
--Lespairs:
4*-2*12*0*6*
Onproposera toutd’abordunesolutionutilisantdesexpressions lambda,puisunesolutionutilisantdesréférencesàdesméthodesqu’onécrira.
Lapremière solution consiste simplement à utiliser commedeuxième argument de laméthodeaffichage_selectifl’unedesexpressionslambdasuivantes:ee->ee<0
ee->2*(ee/2)==ee)
D’oùlesinstructionssupplémentaires:System.out.print("--Lesnegatifs:");
affichage_selectif(tab,ee->ee<0);
System.out.print("--Lespairs:");
affichage_selectif(tab,ee->2*(ee/2)==ee);
La deuxième solution requiert que l’on puisse utiliser à la place des expressionslambda, la référence d’une méthode. Ici, nous utiliserons tout naturellement desméthodes statiques, recevantunargumentde type int et renvoyantun résultatde typebooléen.NouslesnommeronsfiltragePositifs,filtrageNegatifsetfiltragePairs.Voicicequepourraitêtreleprogrammecompletdanslequelnousavonsfaitfigurerlesdeuxsolutions(expressionlambdaetréférence):importjava.util.function.*;
publicclassAffichage
{publicstaticvoidmain(String[]args)
467
{int[]tab={1,4,-2,9,-3,5,-3,12,7,-11,0,6};
System.out.println("--Lespositifs:");
affichage_selectif(tab,ee->ee>0);//lambda
affichage_selectif (tab, Affichage::filtragePositifs) ; //
reference
System.out.println("--Lesnegatifs:");
affichage_selectif(tab,ee->ee<0);//lambda
affichage_selectif (tab, Affichage::filtrageNegatifs) ; //
reference
System.out.println("--Lespairs:");
affichage_selectif(tab,ee->2*(ee/2)==ee);//lambda
affichage_selectif (tab, Affichage::filtragePairs) ; //
reference
}
publicstaticvoidaffichage_selectif(int[]t,IntPredicatef)
{for(intval:t)if(f.test(val))System.out.print(val+"*
");
System.out.println();
}
publicstaticBooleanfiltrageNegatifs(intn){returnn<0;}
publicstaticBooleanfiltragePositifs(intn){returnn>0;}
publicstaticBooleanfiltragePairs(intn){return2*(n/2)==n;}
}
--Lespositifs:
1*4*9*5*12*7*6*
1*4*9*5*12*7*6*
--Lesnegatifs:
-2*-3*-3*-11*
-2*-3*-3*-11*
--Lespairs:
4*-2*12*0*6*
4*-2*12*0*6*
468
171L’interfaceComparator
OndisposedelaclassePointdéfinieainsi:classPoint
{publicPoint(intx,inty){this.x=x;this.y=y;}
publicintgetX(){returnx;}
publicintgetY(){returny;}
publicvoidaffiche()
{System.out.print("["+x+","+y+"]");}
privateintx,y;
}
Écrireuneméthodestatiquenommée traiteListe recevantenpremierargumentunelistedepointssurlaquelleelleréalisesuccessivementtroisopérationsparamétréesparlestroisargumentssuivants:
•unesélectiondesélémentsréalisantunecondition;
•untri(suivantuncritèrevariable)desélémentssélectionnés;
•unaffichagedesélémentsainsitriés.LaméthodetraiteListes’utiliseraainsi(letypedesargumentsétantàpréciser):
traiteListe(liste,selection,tri,affichage);
Onutiliseracetteméthode:
•poursélectionnerlespointsd’abscissepositive,lestriersurlesvaleursdeleursabscissesetlesaffichersuivantcetteforme:[2,5][2,3][6,-3]
• pour sélectionner tous les éléments, les trier suivant la somme de leurscoordonnéesetlesafficherainsi:(abs=-3,ord=4)(abs=6,ord=-3)(abs=2,ord=3)
Ici,onéviterad’utiliserdesstreams.
Le premier argument de la méthode traiteListe est naturellement de typeArrayList<Point>.Lessuivantssontdesimplémentationsdesinterfacesfonctionnelles
469
prédéfiniessuivantes:
•Predicate<Point>,dontlaméthodefonctionnelleesttest;
•Comparator<Point>;
•Consumer<Point>dontlaméthodefonctionnellesenommeaccept.Commel’énoncénousimposedenepasutiliserdestream, ilestnécessairedecréerlocalement une liste nommée ici liste2 contenant les éléments sélectionnés pour lasoumettreautri.VoicicequepourraitêtrenotreméthodetraiteListe:publicstaticvoidtraiteListe(ArrayList<Point>liste,
Predicate<Point>selec,
Comparator<Point>comp,
Consumer<Point>aff)
{ArrayList<Point>liste2=newArrayList<Point>();
liste.forEach(ee->{if(selec.test(ee))liste2.add(ee);});
liste2.sort(comp);
liste2.forEach(ee->aff.accept(ee));
}
Pourlepremiertraitement,lasélectionpeutsefaireàl’aidedel’expressionlambda:ee->ee.getX()>0
Pour le comparateur à fournir en troisième argument, nous avons plusieurspossibilités:
•utiliseruneexpressionlambdapourimplémenterlaméthodefonctionnellecomparede l’interface Comparator<Point>, ce qui conduit à une expression assezcompliquée,notammentàcausedesconversionsenIntegerduesàcequelaméthodegetXfournitunintetnonunInteger:(pp1,pp2)->((Integer)(pp1.getX()))
.compareTo(((Integer)(pp2.getX())))
• Utiliser la méthodeComparator.comparing pour créer un comparateur à partird’uneexpressionlambda:Comparator.comparing(pp->pp.getX())
•UtiliseràlafoislaméthodeComparator.comparingetuneréférencedeméthode:Comparator.comparing(Point::getX)
Nous choisirons la dernière pour sa simplicité (mais, à titre indicatif, nousprogrammeronségalementlapremièredansnotreexemplecomplet).Quant à l’affichage, il peut se faire simplement ici à l’aide de la méthodePoint::affiche.Pour le deuxième traitement, il nous faut sélectionner tous les points ; nous utilisons
470
l’expressionlambda:xx->true
Pourlecomparateur,nousnepouvonsplusutiliserderéférenceàuneméthodepuisquelacomparaionportedorénavantsurlasommedescoordonnées(àmoinsdecréeruneméthode supplémentaire à cet effet). Nous utiliserons donc la méthodeComparator.comparingdecettemanière:
Comparator.comparing(xx->xx.getX()+xx.getY())
Enfin,l’affichagenepeutplusrecouriràlaméthodeaffichedelaclassePoint.Nousaurionspucréeruneméthodestatiqueettransmettresaréférenceenargument.Ici,nousavonschoisiuneexpressionlambda.Endéfinitive,notreprogrammepourraitseprésenterainsi:importjava.util.function.*;
importjava.util.*;
publicclassTraiteListe
{publicstaticvoidmain(String[]args)
{Point[]tab={newPoint(2,5),newPoint(-3,4),
newPoint(2,3),newPoint(6,-3)};
ArrayList<Point>l=newArrayList<Point>();
for(Pointp:tab)l.add(p);
//selectiondespointsd'abscissepositive,
//trisurl'abscisseutilisantComparator.comparing
traiteListe(l,ee->ee.getX()>0,
Comparator.comparing(Point::getX),
Point::affiche);
System.out.println();
//memechoseavecuncomparateursousformed’expressionlambda
traiteListe(l,ee->ee.getX()>0,
(pp1,pp2)->((Integer)(pp1.getX()))
.compareTo(((Integer)(pp2.getX()))),
Point::affiche);
System.out.println();
//tridetouslespointssuivantlasommedescoordonnees
//avecComparator.comparing
traiteListe(l,xx->true,
Comparator.comparing(xx->xx.getX()+xx.getY()),
(xx->System.out.print("(abs="+xx.getX()
+",ord="+xx.getY()+")")));
}
471
publicstaticvoidtraiteListe(ArrayList<Point>liste,
Predicate<Point>selec,
Comparator<Point>comp,
Consumer<Point>aff)
{ArrayList<Point>liste2=newArrayList<Point>();
liste.forEach(ee->{if(selec.test(ee))liste2.add(ee);});
liste2.sort(comp);
liste2.forEach(ee->aff.accept(ee));
}
}
classPoint
{publicPoint(intx,inty){this.x=x;this.y=y;}
publicintgetX(){returnx;}
publicintgetY(){returny;}
publicvoidaffiche(){System.out.print("["+x+","+y+"]
");}
privateintx,y;
}
[2,5][2,3][6,-3]
[2,5][2,3][6,-3]
(abs=-3,ord=4)(abs=6,ord=-3)(abs=2,ord=3)(abs=2,
ord=5)
472
172Lesméthodesusuellesdesstreams
Queproduitleprogrammesuivant:importjava.util.stream.*;
publicclassExoStream
{publicstaticvoidmain(Stringargs[])
{int[]tab={3,5,-3,8,12,4,7,4,8,3};
long nb = IntStream.of(tab).filter(xx -> xx
>0).count();//1
System.out.println("nb="+nb);
IntStream.of(tab).filter(xx -> xx >
3).sorted()//2
.forEach(xx->System.out.print(xx+""));
System.out.println();
IntStream.of(tab).filter(xx -> xx
>3).sorted().distinct()//3
.forEach(xx->System.out.print(xx+""));
int s =IntStream.of(tab).map(xx ->
Math.abs(xx))//4
.map(xx->xx*xx).sum();
System.out.println("\nresultat="+s);
}
}
L’instruction1créeunstreamàpartirdutableaud’entierstab.Laméthodefilterfiltrelesélémentspositifsetceux-cisontcomptabilisésparlaméthodeterminalecount.L’instruction2créelemêmestream,ensélectionnantcettefoislesélémentsdevaleursupérieureà3,enlestriantsuivantl’ordrenatureletenlesaffichant.L’instruction3faitlamêmechosequel’instruction2aveccettedifférenceque,aprèsletri,onévitedeconserverdesvaleursendouble,graceàlaméthodedistinct.Enfin, l’instruction4effectueunepremière transformationassociantàchaquenombresa valeur absolue, puis une seconde transformation associant à chaque élément soncarré (notez qu’ici on obtiendrait lemême résultat, en supprimant laméthodemap).Enfin,laméthodeterminalesumeffectuelasommedecesderniers.Endéfinitive,ceprogrammeaffiche:nb=9
473
44578812
457812
resultat=405
474
173Traitementdelisteavecunstream
L’exercice 171 proposait de réaliser uneméthode statique de traitement de liste,laquelledevaitnécessairementcréerunenouvelleliste.Écrireunprogrammeeffectuantlesmêmesopérations,enutilisantunstream.Onproposeradeuxsolutions:
•l’uneutilisanttoujourslaméthodetraiteListe;
• l’autre n’utilisant plus cetteméthode et se contentant d’effectuer le traitementdirectementauseindelaméthodemain.
La première démarche consiste simplement à créer un stream dans la méthodetraiteListe:publicstaticvoidtraiteListe(ArrayList<Point>liste,
Predicate<Point>selec,
Comparator<Point>comp,
Consumer<Point>aff)
{liste.stream().filter(selec).sorted(comp).forEach(aff);
}
}
Onnoteraque,cettefois,iln’estplusnécessairedecréerunenouvellelistepuisquelesdifférentes opérations réalisées par le stream n’en modifient pas la source (listed’origine).Leresteducodeestinchangé.Ladeuxièmedémarchepossède,làencore,plusieursvariantessuivantlamanièredontonfournitlesargumentsauxméthodesfilter,sortedetforEach.Voiciuneformulationpossible:importjava.util.*;
publicclassExoTraiteLlistStream
{publicstaticvoidmain(String[]args)
{Pointp1=newPoint(2,5),p2=newPoint(-2,3),
p3=newPoint(6,-3),p4=newPoint(-3,-2);
ArrayList<Point>l=newArrayList<Point>();
l.add(p1);l.add(p2);l.add(p3);l.add(p4);
//sélectiondespointsd'abscissepositive,trisurl'abscisse
l.stream().filter(ee->ee.getX()>0)
475
.sorted(Comparator.comparing(xx->xx.getX()))
.forEach(Point::affiche);
System.out.println();
//tridetouslespointssuivantlasommedescoordonnees
l.stream().sorted (Comparator.comparing (xx -> xx.getX() +
xx.getY()))
.forEach(xx->System.out.print("(abs="+xx.getX()
+",ord="+xx.getY()));
}
}
classPoint
{publicPoint(intx,inty){this.x=x;this.y=y;}
publicintgetX(){returnx;}
publicintgetY(){returny;}
publicvoidaffiche()
{System.out.print("["+x+","+y+"]");}
privateintx,y;
}
[2,5][6,-3]
(abs=-3,ord=-2)(abs=-2,ord=3)(abs=6,ord=-3)(abs=2,ord=5)
476
174Répertoire
OndisposedelaclassePersonnesuivante:classPersonne
{publicPersonne(Stringprenom,Stringnom,intannee)
{ this.nom = nom ; this.prenom = prenom ; annee_naissance =
annee;}
publicStringgetNom(){returnnom;}
publicStringgetPrenom(){returnprenom;}
publicintgetAnnee(){returnannee_naissance;}
privateStringnom,prenom;
privateintannee_naissance;
}
}
Réaliser les opérations suivantes sur un tableau d’objets de type Personne, enutilisantunstream:
•afficherlenomdespersonnesnéesaprès1985;
•afficherlenomdespersonnesnéesavant2000,triésparordrealphabétiquesurleurnom,etafficherleurnombre;
•affichertouslesnomsetprénoms,triésparordrealphabétiquesurleurnometleurprénom.
Sitabdésigneletableaud’objetsenquestion,onpourracréerunstreamassociéàcetableau,àl’aidedelaméthodestatiqueofdelaclasseStream,enprocédantainsi:Stream.of(tab)
Pourlapremièreopération,onluiappliqueraunfiltrepoursélectionnerlespersonnescorrespondantàlaconditionvoulue:.filter(pp->pp.getAnnee()>1985)
Enfin,l’affichageseraprovoquéparl’opérationterminaleforEach:.forEach(pp->System.out.print(pp.getPrenom()+",")
Pourlasecondeopération,onprocéderadefaçonsimilaire,enajoutantuneopérationintermédiaire de tri, à l’aide de la méthode sorted, à laquelle on fournira lecomparateurvoulu.Plusieursdémarchessontpossibles,laplussimpleétantderecourirà la méthodeComparator.comparing en lui fournissant la référence d’une méthode
477
existantdéjadanslaclassePersonne,àsavoiriciPersonne::getNom.En revanche, cette fois, on ne peut plus utiliser forEach pour afficher les nomsdemandéspuisquecetteopérationterminaledéclencheraitl’exécutiondustream,alorsque nous voulons pouvoir l’utiliser pour lui appliquer la méthode count (à moinsd’utiliserdeuxstreamsconsécutifs,l’unpourl’affichage,l’autrepourlecomptage).Onpeut recourirà laméthodepeek qui, tout enattendantunConsumer comme forEach,présente laparticularitéd’êtreuneméthode intermédiaire laissant le stream inchangé(onretrouveensortielestreamfournienentrée).Enfin, pour la troisième opération, on utilisera également la méthode sorted, en luifournissanttoujoursuncomparateurcrééparlaméthodeComparator.comparingmais,cettefois,iln’estpluspossibledeluifourniruneréférencedeméthode(àmoinsd’enécrireunespécialementpourcela).Onprocèderaainsi:sorted(Comparator.comparing(pp->pp.getNom()+pp.getPrenom()))
Voiciunexempledeprogrammecompletcréantun"mini-répertoire"decinqpersonnes(nousn’avonspasrappelélaclassePersonne):importjava.util.stream.*;
importjava.util.*;
publicclassExoRepert
{publicstaticvoidmain(String[]args)
{Personne[]tab={newPersonne("thibault","Rougier",2001),
newPersonne("thomas","Niesseron",1987),
newPersonne("thifaine","Mitenne",1959),
newPersonne("maxime","Forest",1995),
newPersonne("jules","Forest",1995)};
System.out.println("---Nesapres1985:");
Stream.of(tab).filter(pp->pp.getAnnee()>1985)
.forEach(pp->System.out.print(pp.getPrenom()+","));
System.out.println("\n---Nesavant2000:");
longnombre=Stream.of(tab).filter(pp->pp.getAnnee()<2000)
.sorted(Comparator.comparing(Personne::getNom))
.peek(pp->System.out.print(pp.getNom()+""))
.count();
System.out.println("\nIlssont"+nombre);
System.out.println("---Toustriessurnom+prenom:");
Stream.of(tab).sorted(Comparator.comparing
(pp->pp.getNom()+pp.getPrenom()))
.forEach(pp->System.out.print("("+pp.getNom()+","
+pp.getPrenom()+")"));
}
478
}
---Nesapres1985:
thibault,thomas,maxime,jules,
---Nesavant2000:
ForestForestMitenneNiesseron
Ilssont4
---Toustriessurnom+prenom:
(Forest, jules) (Forest, maxime) (Mitenne, thifaine) (Niesseron,
thomas)
(Rougier,thibault)
479
175Répertoire(bis)
Onsupposequ’ondisposedelaclassePersonnedel’exerciceprécédent.Écrire un programme qui, à partir d’une liste de personnes (List<Personne>)utiliseunstreampourafficherl’annéedenaissancedelaplusjeune.Onproposeradeuxformulations:
• l’une ne recourant pas à un comparateur et affichant seulement l’annéeconcernée;
•l’autreaffichantlenom,prénometannéedenaissancedelapersonneconcernée(onpeut,cettefois,utiliseruncomparateur).
Dans les deux démarches imposées, il fautmanifestement recourir à laméthodemind’unstream.Lorsqu’onl’appliqueàunStream<T>,ilestnécessairedeluifourniruncomparateur. En revanche, lorsqu’on l’applique à un IntStream, la méthodemin nedisposed’aucunargumentetutilisel’ordrenatureldesentiers.Danslesdeuxcas,noustravailleronssurunStream<Personne>.Danslepremier,nousutiliseronslaméthodemapToIntpourextraireseulementlesannéesdenaissance,sousformed’unIntStreamauquelnousappliqueronslaméthodemax.Dans le deuxième cas, nous appliquerons directement la méthode min sur ceStream<Personne>,enfournissantuncomparateurapproprié,cequinouspermettraderécupérerl’ensembledesinformationssousformed’unobjetdetypePersonne.Enfin,ilfautajouterque,danslesdeuxcas,laméthodemintientcomptedufaitquelestreampeutêtrevide,enfournissantunobjetdetypeOptionalInt(pourlepremiercas)ouOptional<Personne> (pour le deuxième cas). Il faut alors utiliser la méthodesisPresent pour savoir s’il y a bien présence d’une valeur que l’on récupère, le caséchéant,pargetAsInt(danslepremiercas)ouget(dansledeuxième).En définitive, voici un exemple complet de programme créant, là encore, un mini-répertoire de cinq personnes. Notez que, l’énoncé imposant une liste, nous avonsd’abord créé un tableau de personnes, que nous transformons en liste à l’aide de laméthodeasListdelaclasseutilitaireArrays.importjava.util.*;
publicclassOptionalPersonne
480
{publicstaticvoidmain(String[]args)
{Personne[]tab={newPersonne("thibault","Rougier",2001),
newPersonne("thomas","Niesseron",1987),
newPersonne("thifaine","Mitenne",1959),
newPersonne("maxime","Forest",1995),
newPersonne("jules","Forest",1995)};
List<Personne>liste=Arrays.asList(tab);
// utilisation d'un Stream<Personne> transforme par map en
IntStream
OptionalIntanneeJeune=liste.stream()
.mapToInt(pp->pp.getAnnee()).max();
if(anneeJeune.isPresent())
System.out.println("---Methode1-Leplusjeuneestneen:
"
+anneeJeune.getAsInt());
elseSystem.out.println("---Listevide");
//recherchedeminsurunStream<Personne>
Optional<Personne>personneJeune=liste.stream()
.max(Comparator.comparing(Personne::getAnnee));
if(personneJeune.isPresent())
{Personnepj=personneJeune.get();
System.out.println("---Methode2-Leplusjeuneest:"
+ pj.getNom() + " " + pj.getPrenom() + " " +
pj.getAnnee());
}
elseSystem.out.println("---Listevide");
}
}
classPersonne
{publicPersonne(Stringprenom,Stringnom,intannee)
{this.nom=nom;this.prenom=prenom;annee_naissance=annee;
}
publicStringgetNom(){returnnom;}
publicStringgetPrenom(){returnprenom;}
publicintgetAnnee(){returnannee_naissance;}
privateStringnom,prenom;
privateintannee_naissance;
}
481
---Methode1-Leplusjeuneestneen:2001
---Methode2-Leplusjeuneest:Rougierthibault2001
482
176Reduce
Écrire un programme qui utilise un stream pour générer un nombre donné denombres réels compris dans l’intervalle [0.5, 1.5[ et en calculer le produit. Onafficheralesnombrescomprisdansl’intervalle[1-e,1+e],eétantunepetitevaleur(parexemple0.01).Fairelamêmechoseengénérantdesnombrescomprisdansl’intervalle[0,2[etenfiltrantceuxcomprisentre0.5et1.5.
Nous commençonspar créer un streamd’éléments de typedouble (DoubleStream) àl’aidede laméthodegénératricegenerate, à laquelle on fournit l’expression lambda(laméthoderandomfournitundoublecomprisdansl’intervalle[0,1[):()->(Math.random()+0.5)
Nous limitons lesvaleurs ainsi produites à l’aidede laméthode intermédiaire limit.Ensuite,ilnousfautimprimercertainesvaleurs,sansmodifierlestream;nousutilisonspour cela la méthode peek, à laquelle nous fournissons l’instruction effectuantl’impressiondesvaleursvoulues:.peek(xx->{if(xx>1-EPS&&xx<1+EPS)
System.out.print(xx+"");
})
Enfin,commenousnedisposonspasdeméthode"toutefaite"poureffectuerl’opérationderéductiondemandée(calculduproduitdeséléments),nousrecouronsàlaméthodereduce, en utilisant 1 comme valeur initiale (et donc, comme élément neutre de laréduction)etuneexpressionlambdapour"l’accumulateur":.reduce(1,(xx,yy)->xx*yy)
Ladeuxièmequestion se résoutde façoncomparable. Il faut simplement effectuerunfiltragepréalabledesvaleurstiréesauhasard,avantd’enlimiterlesvaleursparlimit.importjava.util.stream.*;
publicclassExoReduce1
{publicstaticvoidmain(Stringargs[])
{finalintNVALEURS=200;
finaldoubleEPS=1e-2;
483
double produit1 = DoubleStream.generate (() ->
(Math.random()+0.5))
.limit(NVALEURS)
.peek(xx->{if(xx>1-EPS&&xx<1+EPS)
System.out.print(xx+"");
})
.reduce(1,(xx,yy)->xx*yy);
System.out.println("\nProduit1:"+produit1);
doubleproduit2=DoubleStream.generate(()->2*Math.random())
.filter(xx->xx>0.5&&xx<1.5)
.limit(NVALEURS)
.peek(xx->{if(xx>1-EPS&&xx<1+EPS)
System.out.print(xx+"");
})
.reduce(1,(xx,yy)->xx*yy);
System.out.println("\nProduit2:"+produit2);
}
}
0.9975467498452386 0.998138364621028 0.99188663131875
1.0083743070821198
0.9928318680893621
Produit1:2.697255477885424E-5
0.9902255816404553 0.9940031238252993 0.9997538300614459
1.003265020181365
Produit2:5.127710428111979E-4
484
177CollectetCollectors
Onsupposequ’ondisposedelaclasePointsuivante:classPoint
{publicPoint(intx,inty){this.x=x;this.y=y;}
publicintgetX(){returnx;}
publicintgetY(){returny;}
privateintx,y;
}
Écrireunprogrammequiréaliselesopérationssuivantes:
•À partir d’un tableau de points, créer un ensemble formé des points dont lesabscissessontpositives;
• À partir d’un tableau d’entiers, créer tout d’abord un Stream<Point> dontchaque élément a pour abscisse un élément du tableau et pour ordonnée sondouble;utilisercestreampourcréerunmapdanslequelàchaqueclécorrespondlalistedespointsayantcetteclécommeabscisse.
• À partir d’un tableau de chaînes, créer une chaîne unique formée de laconcaténationdeschaînesde longueur supérieureà4, séparéespar lecaractère"|".
• À partir de ce même tableau de chaînes, créer un map où à chaque cléreprésentantunelettredel’aphabetcorrespondlalistedesmotscommençantparcettelettre.
Si notre tableau de points se nomme tabPoints, on voit qu’il faut créer un stream àl’aidedelaméthodeof:Stream.of(tabPoints)
etlefiltreraveclacondition:.filter(xx->getX()>0)
puis, pour créer l’ensemble voulu, on utilise la méthode collect en lui fournissantcommeparamètreCollectors.toSet(),cequipermetdecollecterlesvaleursdustreamainsiobtenudansunSet<Point>.
485
Pour la seconde question, nous créons leStream<Point> demandé en appliquant laméthodemapdecettemanière:.map(xx->newPoint(xx,2*xx))
Puis,commeprécédemment,nouscollectonslespointsobtenusdansunelisteàl’aidedelaméthodecollectoràquil’onfournitleparamètreCollectors.toList().Pour la question suivante, nous créons un Stream<String> à partir du tableau dechaînes,puisnousfiltronslesélémentsvouluspar:.filter(xx->xx.length()>4)
Nous concaténons alors les chaînes sélectionnées à l’aide de la méthodeCollectors.joining()fournieenargumentdelaméthodecollect.Pour ladernièrequestion,nouschoisironsdesclésde typeStringplutôtquede typeCharacter, ce qui nous évitera des opératons de conversion dans l’écriture ducomparateur.NouscréeronsdoncunMap<String,List<String>,làencoreàl’aidedela méthode collect, mais cette fois nous employons Collectors.groupingBy poureffectuerleregroupementvoulu,lacléétantlapremièrelettredeséléments.Voicicequepourraitêtreleprogramme.importjava.util.*;
importjava.util.stream.*;
publicclassExoCollect
{publicstaticvoidmain(String[]args)
{ Point[] tabPoints = { new Point(2, 4), new Point(3, 8), new
Point(1,3),
newPoint(-2,4),newPoint(3,8),newPoint(1,3)};
Set<Point>ens=Stream.of(tabPoints).filter(xx->xx.getX()>0)
.collect(Collectors.toSet());
System.out.print("Ensemble:");
ens.forEach(pp->System.out.print("["+pp.getX()+","
+pp.getY()+"]"));
Integer[]tab={2,15,-3,2,-5,23,-8,12};
List<Point>liste=Stream.of(tab).map(xx->newPoint(xx,2*xx))
.collect(Collectors.toList());
System.out.print("\nListe:");
liste.forEach (pp -> System.out.print ("["+pp.getX()+",
"+pp.getY()+"]"));
String[]mots={"bonjour","hello","buongiorno","hi","chao",
"bomdia","gutentag"};
String mots_longs =
Stream.of(mots).collect(Collectors.joining("|"));
System.out.println("\nchainedesmotslongs:"+mots_longs);
486
Map<String,List<String>>map=Stream.of(mots)
.filter(xx->xx.length()>4)
.collect(Collectors.groupingBy(xx->(xx.substring(0,1))));
System.out.println("MAP:"+map);
}
}
classPoint
{publicPoint(intx,inty){this.x=x;this.y=y;}
publicintgetX(){returnx;}
publicintgetY(){returny;}
privateintx,y;
}
Ensemble:[1,3][2,4][1,3][3,8][3,8]
Liste:[2,4][15,30][-3,-6][2,4][-5,-10][23,46][-8,-16]
[12,24]
chaine des mots longs : bonjour|hello|buongiorno|hi|chao|bom
dia|gutentag
MAP:{b=[bonjour,buongiorno,bomdia],g=[gutentag],h=[hello]}
487
AnnexeA
Lesconstantesetfonctionsmathématiques
EllessontfourniesparlaclasseMath.Lesanglessonttoujoursexprimésenradians.
Constante(double) ValeurE 2.718281828459045PI 3.141592653589793
Fonction Rôle En-têtes
abs Valeurabsolue
doubleabs(doublea)floatabs(floata)intabs(inta)longabs(longa)
acos Arccosinus(angledansl’intervalle[-1,1]) doubleacos(doublea)
asin Arcsinus(angledansl’intervalle[-1,1]) doubleasin(doublea)
atan Arctangente(angledansl’intervalle[-pi/2,pi/2]) doubleatan(doublea)
atan2 Arctangente(a/b)(angledansl’intervalle[-pi/2,pi/2])
doubleatan2(doublea,doubleb)
ceil Arrondiàl’entiersupérieur doubleceil(doublea)cos Cosinus doublecos(doublea)exp Exponentielle doubleexp(doublea)
488
floor Arrondiàl’entierinférieur doublefloor(doublea)
IEEEremainder Restedeladivisiondexpary doubleIEEEremainder(doublex,doubley)
log Logarithmenaturel(népérien) doublelog(doublea)
max Maximumdedeuxvaleurs
doublemax(doublea,doubleb)floatmax(floata,floatb)intmax(inta,intb)longmax(longa,longb)
min Minimumdedeuxvaleurs
doublemin(doublea,doubleb)floatmin(floata,floatb)intmin(inta,intb)longmin(longa,longb)
pow Puissance(ab) doublepow(doublea,doubleb)
random Nombrealéatoiredansl’intervalle[0,1[ doublerandom()
rint Arrondiàl’entierleplusproche doublerint(doublea)
round Arrondiàl’entierleplusproche longround(doublea)intround(floata)
sin Sinus doublesin(doublea)sqrt Racinecarrée doublesqrt(doublea)tan Tangente doubletan(doublea)
toDegrees Conversionderadiansendegrés doubletoDegrees(doubleaRad)
toRadians Conversiondedegrésenradians doubletoRadians(doubleaDeg)
489
AnnexeB
Lescomposantsgraphiquesetleursméthodes
Nous présentons ici les principales classes et méthodes des paquetages java.awt etjavax.swing,enparticuliercellesquisontutiliséesdanslesexercicesdecetouvrage.Onnoteraque:
•lorsqu’uneméthodeestmentionnéedansuneclasse,ellen’estpasrappeléedanslesclassesdérivées;
• lorsqu’une classe se révèle inutilisée en pratique (exemple Window, Frame,Dialog), ses méthodes n’ont été mentionnées que dans ses classes dérivées ; parexemple, la méthode setTitle est définie dans la classe Frame mais elle n’estindiquéequedanslaclasseJFrame.
Nous vous fournissons d’abord l’arborescence des classes concernées, avant d’endécrire les différentesméthodes, classe par classe (pour chacune, nous rappelons lalistedesesancêtres).
490
1 LesclassesdecomposantsLesclassesprécédéesd’unastérisque(*)sontabstraites.*Component*ContainerPanelAppletJApplet
WindowJWindowFrameJFrame
DialogJDialog
JComponentJPanelAbstractButtonJButtonJToggleButtonJCheckBoxJRadioButton
JMenuItemJCheckBoxMenuItemJRadioButtonMenuItemJMenu
JLabelJTextComponentJTextFieldJList
JcomboBox
491
JMenuBarJPopupMenuJScrollPaneJToolBar
492
2 Lesméthodes
*ComponentComponent()
void add(PopupMenumenuSurgissant)void addFocusListener(FocusListenerécouteur)void addKeyListener(KeyListenerécouteur)void addMouseListener(MouseListenerécouteur)void addMouseMotionListener(MouseMotionListenerécouteur)Color getBackground()Rectangle getBounds()Font getFont()FontMetrics getFontMetrics(Fontfonte)Color getForeground()Graphics getGraphics()int getHeight()Dimension getSize()Toolkit getToolkit()int getX()int getY()int getWidth()boolean hasFocus()boolean imageUpdate(Imageimage,intflags,intx,inty,intlargeur,inthauteur)void invalidate()boolean isEnabled()boolean isFocusTraversable()boolean isVisible()void paint(GraphicscontexteGraphique)void setBackground(colorcouleurFond)void setBounds(Rectangler)void setBounds(intx,inty,intlargeur,inthauteur)void setCursor(CursorcurseurSouris)void setEnabled(booleanactivé)
493
void setFont(Fontfonte)void setForeground(ColorcouleurAvantPlan)void setSize(Dimensiondim)void setSize(intlargeur,inthauteur)void setVisible(booleanvisible)void update(GraphicscontexteGraphique)void validate()
*Container(Component)Container()
Component add(Componentcomposant)void add(Componentcomposant,Objectcontraintes)Component add(Componentcomposant,intrang)Component add(Componentcomposant,Objectcontraintes,intrang)void setLayout(LayoutManagergestionnaireMiseEnFormevoid remove(intrang)void remove(Componentcomposant)void removeAll()
Applet(Panel-Component-Container)applet()
void destroy()URL getCodeBase()Image getImage(URLadresseURL)Image getImage(URLadresseURL,StringnomFichier)String getParameter(StringnomParamètre)void init()void resize(Dimensiondim)void resize(intlargeur,inthauteur)void start()void stop()
JApplet(Applet-Panel-Component-Container)JApplet()
Container getContentPane()void setJMenuBar(JMenuBarbarreMenus)void setLayout(LayoutManagergestionnaireMiseEnForme)
494
JFrame(Frame-Window-Component-Container)JFrame()JFrame(Stringtitre)
Container getContentPane()Toolkit getToolkit()void setContentPane(Containercontenu)void setDefaultCloseOperation(intoperationSurFermeture)void setJMenuBar(JMenuBarbarreMenus)void setLayout(LayoutgestionnaireMiseEnForme)void setTitle(Stringtitre)//héritéedeFramevoid update(GraphicscontexteGraphique)
JDialog(Dialog-Window-Container)JDialog(Dialogpropriétaire,booleanmodale)JDialog(Framepropriétaire,booleanmodale)JDialog(Dialogpropriétaire,Stringtitre,booleanmodale)JDialog(Framepropriétaire,Stringtitre,booleanmodale)
void dispose()Container getContentPane()void setDefaultCloseOperation(intoperationSurFermeture)void setLayout(LayoutManagergestionnaireMiseEnForme)void setJMenuBar(JMenuBarbarreMenus)void setTitle(Stringtitre)//héritéedeDialogvoid show()void update(GraphicscontexteGraphique)
JComponent(Container-Component)JComponent()
Graphics getGraphics()Dimension getMaximumSize()Dimension getMinimumSize()Dimension getPreferredSize()void paintBorder(GraphicscontexteGraphique)void paintChildren(GraphicscontexteGraphique)void paintComponent(GraphicscontexteGraphique)void revalidate()void setBorder(Borderbordure)
495
void setMaximumSize(Dimensiondimensions)void setMinimumSize(Dimensiondimensions)void setPreferredSize(Dimensiondimensions)void setToolTipText(StringtexteBulleDAide)
JPanel(Jcomponent-Container-Component)JPanel()JPanel(LayoutManagergestionnaireMiseEnForme)
AbstractButton(Jcomponent-Container-Component)AbstractButton()
void addActionListener(ActionListenerécouteur)void addItemListener(ItemListenerécouteur)String getActionCommand()String getText()boolean isSelected()void setActionCommand(StringchaineDeCommande)void setEnabled(booleanactivé)void setMnemonic(charcaractèreMnémonique)void setSelected(booleansélectionné)void setText(Stringlibellé)
JButton(AbstractButton-JComponent-Container-Component)JButton()JButton(Stringlibellé)
JCheckBox(JToggleButton-AbstractButton-JComponent-Container-Component)
JCheckBox()JCheckBox(Stringlibellé)JCheckBox(Stringlibellé,booleansélectionné)
JRadioButton(JToggleButton-AbstractButton-JComponent-Container-Component)
JRadioButton(Stringlibellé)JRadioButton(Stringlibellé,booleansélectionné)
JLabel(JComponent-Container-Component)
496
JLabel(Stringtexte)void setText(Stringlibellé)
JTextField(JTextComponent-JComponent-Container-Component)JTextField()JTextField(intnombreColonnes)JTextField(StringtexteInitial)JTextField(StringtexteInitial,intnombreColonnes)
DocumentgetDocument() //héritéedeJTextComponent
String getText() //héritéedeJTextComponent
void setColumns(intnombreCaractères)
void setEditable(booleanéditable) //héritéedeJTextComponent
void setText(Stringtexte) //héritéedeJTextComponent
JList(JComponent-Container-Component)JList()JList(Object[]données)
void addListSelectionListener(ListSelectionListenerécouteur)void setSelectedIndex(intrang)int getSelectedIndex()int[] getSelectedIndices()Object getSelectedValue()Object[] getSelectedValues()boolean getValueIsAdjusting()void setSelectedIndex(intrang)void setSelectedIndices(int[]rangs)void setSelectionMode(intmodeDeSelection)void setVisibleRowCount(intnombreValeurs)
JComboBox(JComponent-Container-Component)JComboBox()JComboBox(Object[]données)
void addItem(ObjectnouvelleValeur)
497
int getSelectedIndex()Object getSelectedItem()void insertItemAt(ObjectnouvelleValeur,intrang)void removeItem(ObjectvaleurASupprimer)void removeItemAt(intrang)void removeAllItems()void setEditable(booleanéditable) //héritéedeJTextComponentvoid setSelectedIndex(intrang)
JMenuBar(JComponent-Container-Component)JMenuBar()
JMenu add(JMenumenu)JMenu getMenu(intrang)
JMenu(JMenuItem-AbstractButton-JComponent-Container-Component)
JMenu()JMenu(StringnomMenu)
JMenuItem add(Actionaction)JMenuItem add(JMenuItemoption)void addMenuListener(MenuListenerécouteur)void addSeparator()KeyStroke getAccelerator()void insert(Actionaction,intrang)void insert(JMenuItemoption,intrang)void insertSeparator(intrang)boolean isSelected()void remove(intrang)void remove(JMenuItemoption)void removeAll()void setAccelerator(KeyStrokecombinaisonTouches)void setEnabled(booleanactivé)void setSelected(booleansélectionné)
JPopupMenu(JComponent-Container-Component)JPopupMenu()JPopupMenu(Stringnom)
JMenuItem add(Actionaction)
498
JMenuItem add(JMenuItemoption)void addPopupMenuListener(PopupMenuListenerécouteur)void addSeparator()void insert(Actionaction,intrang)void insert(Componentcomposant,intrang)void remove(Componentcomposant)void setVisible(booleanvisible)void show(Componentcomposant,intx,inty)
JMenuItem(AbstractButton-JComponent-Container-Component)JMenuItem()JMenuItem(StringnomOption)JMenuItem(Iconicône)JMenuItem(StringnomOption,Iconicône)JMenuItem(StringnomOption,intcaractèreMnémonique)
void setAccelerator(KeyStrokecombinaisonTouches)keyStroke getAccelerator()
JCheckBoxMenuItem(JMenuItem-AbstractButton-JComponent-Container-Component)
JChekBoxMenuItem()JChekBoxMenuItem(StringnomOption)JChekBoxMenuItem(Iconeicône)JChekBoxMenuItem(StringnomOption,Iconicône)JChekBoxMenuItem(StringnomOption,booleanactivé)JChekBoxMenuItem(StringnomOption,Iconicône,booleanactivé)
JRadioButtonMenuItem(JMenuItem-AbstractButton-JComponent-Container-Component)
JRadioButtonMenuItem()JRadioButtonMenuItem(StringnomOption)JRadioButtonMenuItem(Iconeicône)JRadioButtonMenuItem(StringnomOption,Iconicône)JRadioButtonMenuItem(StringnomOption,booleanactivé)JRadioButtonMenuItem(StringnomOption,Iconicône,booleanactivé)
499
JScrollPaneJScrollPane()JScrollPane(Component)
JToolBarJToolBar()JToolBar(intorientation)
JButton add(Actionaction)void addSeparator()void addSeparator(Dimensiondimensions)boolean isFloatable()void remove(Componentcomposant)void setFloatable(booleanflottante)
500
AnnexeC
Lesévénementsetlesécouteurs
Nous vous fournissons tout d’abord deux tableaux de synthèse, le premier pour lesévénementsdebasniveau,lesecondpourlesévénementssémantiques.Ilsfournissentpourchacunedesprincipalesinterfacesécouteurscorrespondantes:
•lenomdel’interfaceécouteuretlenomdelaclasseadaptateur(sielleexiste),
•lesnomsdesméthodesdel’interface,
•letypedel’événementcorrespondant,
•lesnomsdesprincipalesméthodesdel’événement,
•lescomposantsconcernés.Voustrouverezensuitelesen-têtescomplètesdesméthodesdesclassesévénement.
501
3 Lesévénementsdebasniveau
502
4 LesévénementssémantiquesDans la dernière colonne de ce tableau, les termes génériques Boutons et Menusdésignentlesclassessuivantes
•Boutons:JButton,JCheckBox,JRadioButton,
•Menus:JMenu,JMenuItem,JCheckBoxMenuItem,JRadioButtonMenuItem.
503
5 Lesméthodesdesévénements
MouseEventint getClickCount()Component getComponent()int getModifiers()Object getSource()int getX()int getY()Point getPoint()boolean isAltDown()boolean isAltGraphDown()boolean isControlDown()boolean isMetaDown()boolean isPopupTrigger()boolean isShiftDown()
KeyEventComponent getComponent()Object getSource()char getKeyChar()int getKeyCode()String getKeyText(intcodeToucheVirtuelle)int getModifiers()boolean isAltDown()boolean isAltGraphDown()boolean isControlDown()boolean isMetaDown()boolean isShiftDown()
FocusEventComponent getComponent()Object getSource()boolean isTemporary()
504
WindowEventComponent getComponent()Object getSource()Window getWindow()
ActionEventObject getSource()String getActionCommand()int getModifiers()
ItemEventObject getSource()Object getItem()int getStateChanged()
ListSelectionEventObject getSource()boolean getValueIsAdjusting()
DocumentEventDocument getDocument()
MenuEventObject getSource()
PopuMenuEventObject getSource()
505
AnnexeD
LaclasseClavier
Voici la listedelaclasseClavierprésentesur lesiteWebd’accompagnementetquevouspouvezutiliserpourlasolutionàcertainsdesexercicesdecetouvrage.Ellefournitdesméthodespermettantdeliresuruneligneuneinformationdel’undestypesint,float,doubleouString.Laméthodedelectured’unechaîneestutiliséeparlesautrespourlirelaligne.
//classefournissantdesfonctionsdelectureauclavier
importjava.io.*;
publicclassClavier
{publicstaticStringlireString()//lectured’unechaine
{Stringligne_lue=null;
try
{InputStreamReaderlecteur=newInputStreamReader(System.in);
BufferedReaderentree=newBufferedReader(lecteur);
ligne_lue=entree.readLine();
}
catch(IOExceptionerr)
{System.exit(0);
}
returnligne_lue;
}
publicstaticfloatlireFloat()//lectured’unfloat
{floatx=0;//valeuralire
try
{Stringligne_lue=lireString();
x=Float.parseFloat(ligne_lue);
506
}
catch(NumberFormatExceptionerr)
{System.out.println("***Erreurdedonnee***");
System.exit(0);
}
returnx;
}
publicstaticdoublelireDouble()//lectured’undouble
{doublex=0;//valeuralire
try
{Stringligne_lue=lireString();
x=Double.parseDouble(ligne_lue);
}
catch(NumberFormatExceptionerr)
{System.out.println("***Erreurdedonnee***");
System.exit(0);
}
returnx;
}
publicstaticintlireInt()//lectured’unint
{intn=0;//valeuralire
try
{Stringligne_lue=lireString();
n=Integer.parseInt(ligne_lue);
}
catch(NumberFormatExceptionerr)
{System.out.println("***Erreurdedonnee***");
System.exit(0);
}
returnn;
}
//programmedetestdelaclasseClavier
publicstaticvoidmain(String[]args)
{System.out.println("donnezunflottant");
floatx;
x=Clavier.lireFloat();
System.out.println("mercipour"+x);
System.out.println("donnezunentier");
507
intn;
n=Clavier.lireInt();
System.out.println("mercipour"+n);
}
}
Notez que, en cas d’exception de type IOException (rare !), on se contented’interrompreleprogramme.Sinousn’avionspastraitécetteexception,nousaurionsdû la déclarer dansune clause throws, ce qui aurait obligé l’utilisateur de la classeClavieràlaprendreencharge.La lecture des informations de type entier ou flottant utilise la méthodeClavier.lireString,ainsique lesméthodesdeconversiondechaînes Integer.parseInt,Float.parseFloat et Double.parseDouble. Nous devons traiter l’exceptionNumberFormatExceptionqu’ellessontsusceptiblesdegénérer.Ici,nousaffichonsunmessageetnousinterromponsleprogramme.
508
PoursuivretouteslesnouveautésnumériquesduGroupeEyrolles,retrouvez-noussurTwitteretFacebook@ebookEyrollesEbooksEyrolles
Etretrouveztouteslesnouveautéspapiersur@EyrollesEyrolles
509