[Plan
du site] Vous êtes ici --- > Le Site
du Zéro > Les
tutoriels > Officiels >
Programmation
> Programmation
en Java > Lecture du tutoriel
Programmation en Java
Bonjour à tous, amis Zéros !
Bienvenue dans mon cours de programmation en Java. C'est un langage très
utilisé, notamment par un grand nombre de programmeurs professionnels, ce qui en
fait un langage incontournable actuellement.
Voici les caractéristiques
de Java en quelques mots :
- Java est un langage de programmation moderne développé par Sun
Microsystems. Il ne faut surtout pas le confondre avec Javascript
(petit langage de scripts utilisé sur les sites web), car Java n'a rien à
voir.
- Une de ses plus grandes forces est son excellente portabilité : une fois
votre programme créé, il fonctionnera automatiquement sous Windows, Mac,
Linux, etc.
- On peut faire de nombreuses sortes de programmes avec Java :
- des applications, sous forme de fenêtre ou de
console ;
- des applets, qui sont des programmes Java
incorporés à des pages web ;
- des applications pour appareils mobiles, avec
J2ME ;
- et bien d'autres ! J2EE, JMF, J3D pour la 3D...
Comme vous
le voyez, Java permet de réaliser une très grande quantité d'applications
différentes ! Mais... comment apprendre un langage si vaste qui offre autant de
possibilités ?

Heureusement,
ce cours est là pour tout vous apprendre de Java à partir de zéro

.
Exemples de programmes réalisés en Java N.B. : je tiens à faire une dédicace
spéciale à ptipilou, zCorrecteur émérite, sans qui ce tuto n'aurait pas vu le
jour !
Un grand merci pour ton travail et ton soutien !
Ce cours est composé des parties suivantes :
-
Partie 1 : Bien commencer en Java
Bon, vous ne connaissez rien à Java ? Eh bien c'est par ici que ça se
passe ! Java est normalement un langage fait pour développer des applications
graphiques, mais pour arriver à cela, nous devons tout de même passer par la
programmation Java en mode console.
Donc, dans cette première partie,
nous allons voir les bases du langage, ainsi que leur
fidèle compagnon Eclipse.
J'ose espérer que vous avez apprécié ce tuto sur les bases du langage
Java ! En tout cas, je me suis bien amusé en le faisant.
Maintenant,
nous allons rentrer dans les méandres de la programmation orientée objet
!
Alors ?... Toujours prêts ?
-
Partie 2 : Java Orienté Objet
Dans cette partie, nous allons aborder la programmation orientée objet.
Concept de programmation extrêmement puissant et pratique.
En effet, vous
verrez qu'avec ce type de programmation, vous pourrez créer, utiliser, recréer
divers objets et les utiliser dans un but que vous seuls aurez décidé.
J'aborderai ce que j'ai pu voir durant ma
formation, et uniquement cela... Mais je vous rassure : il y a déjà du
travail...
Cette partie sera extrêmement riche en concepts,
vocabulaire et méthodologie. Entre autres, vous saurez programmer en
orienté objet, vous pourrez enregistrer vos objets dans
des fichiers...
J'ajouterai aussi quelques notions de modélisation.
Ceci dans le but de vous familiariser avec la façon de schématiser des objets
et leurs interactions entre eux. Nous y reviendrons, mais il s'agira de
diagrammes de classes utilisés avec le langage
UML (Unified
Modeling Language).
Une longue introduction ne servirait à rien...
passons donc tout de suite à la première partie.
J'espère que cette partie vous a plu et que vous avez appris plein de
bonne choses !
J'ai volontairement omis de parler des flux et des threads dans cette partie. Je préfère avoir des
cas bien concrets à vous soumettre pour ça...
Bon : je sais que
beaucoup d'entre vous l'attendent avec impatience, alors voici la partie sur
la programmation événementielle
!
-
Partie 3 : Java et la programmation événementielle
Dans cette partie, nous aborderons la programmation événementielle comme
le stipule le titre.
Par là, entendez
programmation d'interface graphique, ou
IHM, ou encore
GUI.
Nous utiliserons
essentiellement les bibliothèques
Swing et
AWT présentes
d'office dans Java.
Nous verrons ce qui forme, je pense, les
fondements de base ! Nous n'entrerons pas dans les détails, enfin pas trop...

Je
ne vais pas faire de long discours maintenant, je sais que vous êtes
impatients... alors go !
Cette partie est encore en cours d'édition...
Je m'y remets dès
que possible ! Promis...
Partie 1 : Bien commencer en Java
Bon, vous ne connaissez rien à Java ? Eh bien c'est par ici que ça se passe
! Java est normalement un langage fait pour développer des applications
graphiques, mais pour arriver à cela, nous devons tout de même passer par la
programmation Java en mode console.
Donc, dans cette première partie,
nous allons voir les bases du langage, ainsi que leur
fidèle compagnon Eclipse.
Parlons d'Eclipse
Pour ceux qui l'avaient deviné, Eclipse est le petit logiciel
qui va nous permettre de développer nos applications, ou nos applets, et aussi
celui qui va compiler tout ça.
Eh oui ! Java est un langage compilé. Je ne
l'avais pas dit, je crois...

Donc,
notre logiciel va permettre de traduire nos futurs programmes Java en langage
compilé. Mais celui-ci ne peut pas être compris par l'ordinateur. Ce code
compilé s'appelle du
Byte Code. Il n'est compréhensible que par
un environnement Java, vulgairement appelé JRE (
Java
Runtime Environment) disponible sur le site de Sun MicroSystems.
Préambule
Avant toute chose, quelques mots sur le projet
Eclipse.
Eclipse IDE est un environnement de
développement libre permettant potentiellement de créer des projets de
développement mettant en œuvre n'importe quel langage de programmation (C++,
PHP...). Eclipse IDE est principalement écrit en Java.
La spécificité
d'Eclipse IDE vient du fait de son architecture est totalement développée autour
de la notion de
plug-in. Cela signifie que toutes les
fonctionnalités de celui-ci sont développées en tant que plug-in. Pour faire
court, si vous voulez ajouter des fonctionnalités à Éclipse, vous devez
:
- télécharger le plug-in correspondant,
- copier les fichiers spécifiés dans les répertoires spécifiés,
- démarrer Eclipse, et ça y est !
Lorsque vous téléchargez un nouveau plug-in pour
Éclipse, celui-ci se présente souvent comme un dossier contenant généralement
deux sous-dossiers. Un dossier « plugins » et un
dossier « features ». Ces dossiers existent aussi
dans le répertoire d'Éclipse. Il vous faut donc copier le contenu des dossiers
de votre plug-in dans le dossier correspondant dans Éclipse (plugins dans plugins, et features dans features).
C'est pour toutes
ces raisons que j'ai choisi Eclipse comme outil de développement ; de plus, vous
verrez qu'il est relativement simple d'utilisation.
Maintenant que ces
quelques mots ont été dits, je vous invite donc à passer à l'étape suivante.
Téléchargement
Avant de vous lancer dans le téléchargement
d'
Éclipse, vous devez
avant
tout vous assurer d'avoir
un environnement Java, ou JRE
sur votre machine.
Un JRE (ou Java
Runtime Environment) va vous servir à lire les
programmes qui ont été codés en Java. Comme je vous l'ai dit plus tôt, Eclipse
est codé en Java : donc, pour utiliser Eclipse, il vous faut un
JRE.
Rendez-vous donc sur la page de
téléchargement des JRE
sur le site de SUN Microsystem (fondateur du langage).
Choisissez le suivant
(dernière version stable) :
Une
fois cliquez sur "
Download", vous arrivez sur cette page :
Sélectionnez
votre système d'exploitation (ici, j'ai mis
Windows)
et n'oubliez pas de cocher la case : "
I agree to the Java SE Development
Kit 6 License Agreement".
Afin d'accepter la licence !
Vous
voilà arrivé à la dernière ligne droite :
Cliquez
sur le lien du haut pour une installation en ligne, ou celui du bas pour une
installation hors ligne !
Euh... ça veut dire quoi, JSE ?
Alors on va faire simple. Je vous
ai dit plus tôt que Java permet de développer différents types d' applications.
Eh bien il faut des outils différents pour les différents types
d'applications.
- J2SE ou Java 2 Standard
Édition : permet de développer des applications dites
"client", par exemple... euh... Éclipse est une application "client". C'est ce
que nous allons faire dans ce tutoriel.
- J2EE ou Java 2 Enterprise
Édition : permet de développer des applications web en Java.
- J2ME ou Java 2 Micro Édition
: permet de développer des applications pour appareil portable, comme des
téléphones portables, des PDA...
Voilà, fin de
l'aparté...
Vous pouvez maintenant télécharger et installer votre JRE.
Ceci fait, je vous invite donc à télécharger Éclipse IDE sur
cette page.
Accédez à la page de
téléchargement puis choisissez
"Éclipse IDE for java
developers", en choisissant la version d'Eclipse correspondant à votre
OS.
Sélectionnez
maintenant le miroir que vous souhaitez utiliser pour obtenir Éclipse. Voilà,
vous n'avez plus qu'à attendre la fin du téléchargement.
Installation
Maintenant que vous avez un JRE et que vous avez
téléchargé Éclipse, nous allons voir comment nous servir de
celui-ci.
Vous devez maintenant avoir une archive contenant Éclipse.
Décompressez-la où vous voulez, puis, entrez dans ce dossier. Si comme moi vous
êtes sous Windows, vous devriez avoir ceci :
Ensuite, si vous le souhaitez, vous pouvez créer un
raccourci de l'exécutable "
eclipse.exe"
pour pouvoir y accéder plus facilement. Ceci fait, lancez Eclipse.
Peu de
temps après, vous devriez avoir une fenêtre comme celle-ci :
Ici,
Eclipse vous demande dans quel dossier vous souhaitez enregistrer vos projets.
Libre à vous de choisir celui-ci. J'ai coché la
checkbox pour qu'Éclipse se souvienne de ma décision. Mais
lorsque vous créerez des projets, rien ne vous empêche de spécifier un autre
dossier que celui de votre
"workspace".
Une fois cette étape
effectuée, vous arrivez sur la page d'accueil d'Éclipse. Je n'ai jamais trop
regardé ce que celle-ci propose ; donc, si vous avez envie de jeter un coup
d'?il, allez-y.
Présentation rapide de l'interface
Je vais maintenant vous faire faire un tour rapide de
l'interface que vous propose Eclipse, en gros, des éléments dont nous allons
nous servir dans ce tutoriel.
Avant de commencer, regardez bien les raccourcis clavier
présents dans les menus... Ils sont
très utiles et
peuvent vous faire gagner beaucoup de temps !
On est obligés ?
Bien sûr que non, mais
croyez-moi, quand on y a goûté, on ne peut plus s'en passer...
Allez,
trêve de bavardages, on est partis.
Menu "File"
C'est
ici que nous pourrons créer de nouveaux projets Java, enregistrer ceux-ci, les
exporter le cas échéant...
Ici, les raccourcis à retenir sont :
- ALT + SHIFT + N : Nouveau projet
- CTRL + S : enregistrer la classe * Java où on est positionnés
- CTRL + SHIFT + S : tout sauvegarder
- CTRL + W : fermer la classe Java où on est positionnés
- CTRL + SHIFT + W : fermer toutes les classes Java
ouvertes.
*
classe : on y reviendra.
Menu "Edit"
Dans
ce menu, nous pourrons utiliser les commandes "copier", "coller"...
Ici, les
raccourcis à retenir sont :
- CTRL + C : copier la sélection
- CTRL + X : couper la sélection
- CTRL + V : coller la sélection
- CTRL + A : tout sélectionner
- CTRL + F : chercher / remplacer.
Menu
"Window"
Dans
celui-ci, nous pourrons configurer Eclipse selon nos besoins.
La barre de navigation
Nous
avons dans l'ordre :
- 1 : "nouveau" général. Cliquer sur ce bouton revient à faire "Fichier -
Nouveau"
- 2 : enregistrer. Revient à faire CTRL + S.
- 3 : imprimer.
- 4 : exécuter la classe ou le projet spécifié. Nous verrons ceci plus en
détail.
- 5 : créer un nouveau projet Java. Revient à faire "Fichier - Nouveau -
Java project".
- 6 : créer un nouvelle classe dans un projet. Revient à faire "Fichier -
Nouveau - Classe".
Je vous demande maintenant de créer un
nouveau projet Java. Vous devriez arriver à cette fenêtre sans trop de
difficultés :
Renseignez
le nom de votre projet comme je l'ai fait plus haut (encadré 1). Vous pouvez
aussi voir où sera enregistré ce projet (encadré 2).
Un peu plus compliqué
maintenant, vous avez donc un environnement Java sur votre machine, mais dans le
cas où vous en auriez plusieurs, vous pouvez aussi spécifier à Eclipse quel JRE
utiliser pour ce projet.
Vous devriez avoir un nouveau projet dans la fenêtre de gauche,
comme ceci :
Pour
boucler la boucle, ajoutons dès maintenant une nouvelle classe dans ce projet.
Je suis sûr que vous allez y arriver sans moi...
Vous êtes donc devant cette fenêtre :
Dans
l'encadré 1, nous pouvons voir où seront enregistrés nos fichiers Java.
Dans
l'encadré 2, nommez votre classe Java, moi j'ai
sdz1.
Dans l'encadré 3, Eclipse vous demande si
cette classe a un truc particulier. Eh bien oui ! Cochez
"
public static void main(String[] args)", puis
cliquez sur "
finish" (nous allons
revenir sur ce dernier point dans la partie suivante).
Une fois ces
opérations terminées, vous êtes devant ceci :
Alors
avant de commencer à coder, nous allons un peu explorer notre espace de
travail.
Dans l'encadré vert (à gauche), vous trouverez
le dossier de votre projet ainsi que son contenu. Ici, vous pourrez gérer votre
projet comme bon vous semble (ajout, suppression...).
Dans
l'encadré bleu (au centre), je pense que vous avez deviné... C'est ici
que nous allons écrire nos codes sources.
Dans l'encadré rose (en
bas), c'est là que vous verrez apparaître le contenu de vos programmes
ainsi que les erreurs éventuelles !

Et
enfin pour finir,
dans l'encadré violet (à droite), dès lors
que nous aurons appris à coder nos propres fonctions ainsi que des objets, c'est
ici que la liste des méthodes et des variables sera affiché.
Maintenant
que vous avez Eclipse bien en main, nous allons faire un tour rapide des autres
IDE de développement pour Java.
D'autres outils à la hauteur
Surtout gardez en mémoire qu'un IDE est un
outil de développement.
Comment ça, je ne vous l'avais pas dit...
Ce qu'il faut garder en tête, c'est qu'un IDE, comme n'importe quel
outil, est :
- fonctionnel,
- adapté,
- évolutif.
Ce que j'entends par là, c'est que comme un
menuisier, vous utiliserez des outils pour travailler et comme lui, vous
choisirez celui qui vous convient le mieux. Partez du principe que les IDE ont
grosso-modo les mêmes fonctionnalités, compiler en
byte code, exécuter, debugger...
Les IDE
de la liste suivante sont tout aussi adaptés qu'Eclipse pour développer en Java.
A vous de vous faire un avis. Testez, comparez et choisissez.
1. JBuilder
Celui-ci
est idéal pour le développement à grande échelle, pour les projets d'entreprise,
etc.
Il intègre tout un tas de technologies comme
XML,
jsp/servlet, concept d'ingénierie, outils
UML...
C'est simple : je crois même qu'il fait du
café...
Tout ceci fait de lui un outil puissant, mais très lourd pour les
configurations moyennes.
En gros, vous serez peut-être amenés à
l'utiliser, mais en entreprise...
Bon : pour les curieux de nature, vous
pouvez faire un tour
ici.
Ne cherchez pas
de version gratuite, JBuilder est payant. Mais je crois que vous pouvez avoir
une version d'évaluation... A vérifier...
2. NetBeans
NetBeans
est l'IDE créé par
Sun Microsystems, il
contient donc toutes les fonctionnalités d'un bon IDE :
- un debugger,
- un outil de modélisation UML,
- tout le nécessaire pour faire des applications J2EE,
- il est GRATUIT !
- ...
De plus, il semblerait qu'on puisse
customiser l'environnement de celui-ci.
Si vous
voulez vous faire une opinion, ce que je vous conseille vivement, vous pouvez le
télécharger
ici.
Il
existe bien d'autres IDE pour développer en Java, mais ceux-ci (Eclipse compris)
représentent la majorité des IDE utilisés. En voici d'autres, pour information :
- JCreator
- IntelliJ
- Sun ONE Studio
- JDeveloper
- JEdit
- Le Bloc-notes de Windows (vi
sous Linux), ou encore Notepad++.
Qu... Quoi ? On peut faire des programmes Java avec le
Bloc-notes de Windows ?
Tout à fait...
En fait, lorsque vous créerez un programme Java dans votre IDE préféré et
que vous l'exécuterez, celui-ci va pré-compiler votre code Java en
byte code pour qu'ensuite votre
JVM (
Java
Virtual Machine, cf chapitre suivant)
l'interprète et l'exécute.
Mais toutes ces étapes peuvent être faites à
la main, en ligne de commande, mais nous n'en parlerons pas maintenant.
Bon, je crois qu'il est temps pour un petit QCM...
Voilà ! Une partie pénible de terminée !
Par contre,
Eclipse regorge de fonctionnalités. Je n'en ai pas parlé pour le moment car je
pense que vous comprendrez mieux le moment venu, avec un exemple concret
d'utilisation.
J'espère que vous avez encore de l'énergie, car, dans le
chapitre suivant, nous aborderons quelque chose de bien plus passionnant...
Bon ! Tenez-vous prêts pour faire
vos premiers codes Java
! !
Votre premier programme
Dans ce chapitre, nous allons faire nos premiers programmes
en Java.
Mais tout d'abord, quelques explications sur le fonctionnement du
JRE s'imposent.
Alors... go !
Mieux connaitre son environnement Java
Bon : cette partie s'annonce riche en informations et
pour certains, elle sera même "pompeuse"... Mais afin de bien coder en Java, il
vaut mieux comprendre comment tout cela fonctionne.
Dans le JRE, que je
vous ai fait télécharger dans la partie précédente, se trouve le coeur même de
Java. Celui-ci s'appelle la
JVM (pour Java Virtual
Machine) que j'appellerai dorénavant
machine
virtuelle. Celle-ci a pour rôle, comme je vous l'ai dit dans le
chapitre précédent, d'exécuter les programmes Java sur votre machine, c'est ce
qui fait que les programmes Java sont dit
"portables".
Qu'est-ce tu entends par là ?
J'allais y
venir...
Comme je vous l'ai à maintes fois répété, les programmes Java sont,
avant d'être utilisés par la machine virtuelle, pré-compilés en
byte code (par votre IDE ou encore à la main). Ce byte code
n'est compréhensible que par une JVM, et c'est celle-ci qui va faire le lien
entre ce code et votre machine.
Vous aviez sûrement remarqué que sur la
page de téléchargement du JRE, plusieurs liens étaient disponibles :
- un lien pour Windows,
- un lien pour Mac,
- un lien pour Linux.
Ceci car la machine virtuelle Java se
présente différemment selon qu'on se trouve sous Mac, sous Linux ou encore sous
Windows. Par contre le byte code, lui, reste le même quelque soit
l'environnement où a été développé et pré-compilé votre programme
Java.
Conséquence directe :
quel que soit l'OS sous lequel a
été codé un programme Java, n'importe quelle machine pourra l'exécuter si elle
dispose d'une JVM !
Tu n'arrêtes pas de nous rabâcher byte code par-ci...
byte code par-là...
Mais c'est quoi, au juste ?
Eh bien un byte code
- et je dis bien
UN byte code - n'est autre qu'un code
intermédiaire entre votre code Java et le code machine.
Ce code particulier
se trouve dans les fichiers pré-compilés de vos programmes ; en Java, un fichier
source a l'extension
.java et un
fichier pré-compilé a l'extension
.class ; c'est dans ce dernier que vous pourrez voir
du byte code.
Par contre, vos fichiers
.java sont de bêtes fichiers texte, seule l'extension
est changée... Vous pouvez donc les ouvrir, les créer ou encore les mettre à
jour avec... le Bloc-notes de Windows par exemple...

Pour
en revenir à notre byte code, je vous invite à regarder un fichier
.class à la fin de cette partie (vous en aurez au
moins un), mais je vous préviens, c'est illisible ! !
Inutile de vous dire que votre machine est
incapable d'interpréter du byte code !
Alors pourquoi je vous
ai dit
UN byte code ? Tout simplement parce que la
machine virtuelle de Java se moque que le byte code soit à la base du Java ou
autre chose.
Son rôle est d'interpréter ce code et de le faire fonctionner.
Donc, si un jour vous arrivez à faire du byte code avec comme fichier source un
.cpp (pour C++), ça
devrait
(et j'insiste sur le devrait)
fonctionner.
Vous pouvez avoir un aperçu de ce qui devrait
fonctionner sur
cette
page.
Ce qu'il faut retenir :
- la JVM est le coeur de Java,
- celle-ci fait fonctionner vos programmes Java, pré-compilés en byte code,
- les fichiers contenant le code source de vos programmes Java ont
l'extension .java,
- les fichiers pré-compilés correspondant à vos codes sources Java ont
l'extension .class,
- le byte code est un code intermédiaire entre votre programme et votre
machine,
- un programme Java, codé sous Windows peut être pré-compilé sous Mac et
enfin exécuté sous Linux,
- votre machine NE PEUT PAS interpréter du byte code.
Je
pense qu' après cette partie purement théorique, vous avez hâte de commencer à
taper des lignes de code... Mais avant, il me reste quelques choses à vous dire
!
Avant de commencer
Avant de commencer, vous devez savoir que
tous les programmes Java sont composés d'au moins
une classe.
Cette classe doit contenir (applet mise à part)
une méthode
main. Celle-ci est la méthode principale du
programme, c'est elle que la JVM va chercher afin de lancer votre programme.
Pour faire court, c'est le point de départ du programme.
Vous voyez donc son
importance !
Et pour qu'on se comprenne bien, une méthode est une suite d'instructions à
exécuter. Une méthode contient :
- une entête : celle-ci va être
un peu comme la carte d'identité de la méthode. Nous y reviendrons.
- un corps : le contenu de la
méthode. Ce qui va être fait ! Délimité par des accolades {}.
- une valeur de retour : le
résultat que la méthode va retourner. SAUF pour les méthodes
de type void qui ne renvoient
rien. Nous y reviendrons aussi.
Vous verrez, un peu plus tard, qu'un programme
n'est qu'une multitude de classes qui s'utilisent l'une l'autre.
Mais pour
le moment, nous n'allons travailler qu'avec une seule classe.
Dans la
partie précédente, je vous avais demandé de créer un projet Java ; ouvrez-le si
ce n'est pas déjà fait. Donc pour rappel, voici ce que vous aviez sous votre nez
:
Vous
voyez la fameuse classe dont je vous parlais ? Ici, elle s'appelle
sdz1.
Vous pouvez voir que le mot
class est
précédé du mot
public, nous verrons la signification de
celui-ci lorsque nous programmerons des objets.
Pour le moment, ce que
vous devez retenir c'est que votre classe est définie par un mot clé,
class,
qu'elle a un nom ; ici,
sdz1 et que
le contenu de celle-ci est délimité par des accolades,
{}.
Comme je vous l'ai dit plus haut, notre
unique classe contient la méthode
main. La syntaxe de cette
méthode est toujours la même :
Code : Java
1
2
3 |
public static void main(String[] args){
// Contenu de votre classe
}
|
Ce sera entre les accolades de la méthode main que
nous écrirons nos codes sources.
Excuse-nous, mais... Pourquoi tu as écrit "//Contenu
de votre classe" et non "Contenu de votre classe" ?
Bonne question !
Je vous ai dit plus haut que votre programme Java, avant de pouvoir être
exécuté, doit être pré-compilé en byte code. Eh bien la possibilité de forcer le
compilateur à ignorer certaines instructions existe !
On appelle ça des
commentaires et
il existe deux syntaxes :
- les commentaires unilignes :
introduits par les symboles //, ils mettent tous ce qui les
suit en commentaires, du moment que le texte se trouve sur la même ligne que
les //.
Code : Java
1
2
3
4
5
6 |
public static void main(String[] args){
//Un commentaire
//un autre
//Encore un autre
Ceci n'est pas un commentaire ! ! ! !
}
|
- les commentaires multilignes : ils sont introduits par les symboles
/* et se terminent par les symboles */.
Code : Java
1
2
3
4
5
6
7
8
9 |
public static void main(String[] args){
/*
Un commentaire
Un autre
Encore un autre
*/
Ceci n'est pas un commentaire ! !
}
|
D'accord ! Mais ça sert à quoi ?
C'est simple
: au début, vous ne ferez que de très petits programmes.
Mais dès que vous
aurez pris de la bouteille, la taille de ceux-ci, ainsi que le nombre de classes
qui les composera, augmentera.
Vous serez contents de trouver quelques lignes
de commentaires au début de votre classe pour vous dire à quoi elle sert, ou
encore des commentaires dans une méthode qui fait des choses compliquées afin de
savoir ou vous en êtes dans vos traitements...
Il existe en fait
une troisième syntaxe,
mais celle-ci à un but particulier. Elle permettra de générer une documentation
de votre programme ! Une Javadoc (Java Documentation).
Je n'en parlerai que
très peu, et pas dans ce chapitre. Nous verrons cela lorsque nous programmerons
des objets mais, pour les curieux, je vous conseille le très bon
tutoriel
de dworkin.
Je profite de cet aparté sur les commentaires pour vous
donner une astuce bien pratique !
Il vous arrivera forcément à un moment
donné de vouloir mettre une partie de code en commentaire. Dans ce cas, il y a
99,99999999 % de chance que vous choisissiez les commentaires multilignes.
L'inconvénient, c'est que lorsque vous voudrez remettre votre morceau de
code en activité, il vous faudra retirer les
/*
*/. Mais si 5 minutes plus tard, vous voulez remettre la même
portion de code en commentaire

.
Regardez ce morceau de code (il ne fait rien du tout, c'est un exemple)
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
public static void main(String[] args){
String str = "toto";
int entier = 0;
if(entier != 0){
/*
for(int i = 0; i < 20; i++){
System.out.println("oui ! ! ! ! !");
}
//*/
}
}
|
Vous pouvez voir que j'ai
utilisé les commentaires multilignes ! Mais avec une variante...
La ligne qui ferme les commentaires est précédée... de
//.
Mais c'est ce qui met une ligne en commentaires...
C'est idiot de mettre ça là... Ils sont en commentaire
!
Je le sais bien, et c'est là qu'est l'astuce. Pour le moment, les
deux
// sont en commentaires comme les lignes qui se trouvent
entre
/* et
*/.
Mais que se passe-t-il si
j'ajoute un / devant ma première instruction de commentaire ?
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
public static void main(String[] args){
String str = "toto";
int entier = 0;
if(entier != 0){
//*
for(int i = 0; i < 20; i++){
System.out.println("oui ! ! ! ! !");
}
//*/
}
}
|
Eh bien là, ce sont mes
commentaires multilignes qui sont devenus des commentaires, et mes lignes sont
de nouveau actives dans mon code !
Explique-nous ça !
C'est très simple. Le fait
d'ajouter un / devant /* met l'étoile en commentaire... Dans ce cas, il ne
s'agit plus d'un commentaire multilignes, mais uniligne ! Et là je crois que
vous avez deviné l'utilité de cette ligne //*/... Sur celle-ci, c'est
l'instruction de commentaire multiligne fermante qui est en commentaire !
Donc, plus besoin d'ajouter de commentaire, d'en effacer, de les remettre,
de les ré-effacer...
Vous encadrez la portion de code que vous souhaitez
enlever de /* .... //*/ et lorsque vous la voulez à nouveau, vous ajoutez un /
devant l'instruction ouvrante des commentaires ! Et si vous voulez remettre la
même portion de code en commentaire, enlevez le / ajouté devant /* !
A
partir de maintenant et jusqu'à ce que nous programmions des interfaces
graphiques, nous allons faire ce qu'on appelle des
programmes
procéduraux. Cela signifie que le programme se déroulera de façon
procédurale.
Euh... késako ?
En fait, un programme
procédural est un programme qui s'effectue de
haut en bas,
une ligne après l'autre.Bien sûr, il y a des instructions
qui permettent de répéter des morceaux de code, mais le programme en lui même se
terminera une fois arrivé à la fin du code.
Ceci vient en opposition à la
programmation événementielle (ou graphique) qui elle, est basée sur des
événements (clic de souris, choix dans un menu...).
Je pense que j'ai
fait le tour de ce que je voulais vous dire...
Bon : pour votre premier programme, je pense que le traditionnel "Hello
World ! ! " est de mise...
Donc, allons-y !
Votre premier programme
Nous entrons enfin dans le vif du sujet !
Mais ne
vous attendez pas à faire un vrai programme tout de suite...

Maintenant,
vous pouvez taper les lignes suivantes entre les accolades de votre méthode main
:
Code : Java
1 |
System.out.print("Hello World !");
|
N'oubliez surtout pas le ; à la
fin de la ligne !
Toutes les instructions en Java sont suivies d'un
point virgule.
Une fois que vous avez saisi cette ligne de
code dans votre méthode main, vous devez lancer le programme.
Si vous vous
souvenez bien de la présentation du chapitre précédent, vous avez dû cliquer sur
la flèche blanche dans un rond vert :
Si
vous regardez dans votre console, fenêtre en bas sous Eclipse, vous devez avoir
:
Expliquons
un peu cette ligne de code.
Littéralement, elle signifie "la méthode
print() va écrire
Hello World ! en utilisant l'objet
out de la classe
System".
- System : ceci correspond à
l'appel d'une classe qui s'appelle "System". C'est
une classe utilitaire qui permet surtout d'utiliser l'entrée et la sortie
standard.
- out : objet de la classe
System qui gère la sortie standard.
- print : méthode qui écrit
dans la console la chaîne passée en paramètre.
Si vous mettez
plusieurs
System.out.print, voici ce qui ce
passe.
Prenons ce code :
Code : Java
1
2
3 |
System.out.print("Hello World ! !");
System.out.print("My name is");
System.out.print("Cysboy");
|
Lorsque vous l'exécutez,
vous devriez avoir une chaîne de caractères qui se suivent, sans saut à la
ligne. En gros, vous devez avoir ceci dans votre console :
Hello
World ! !My name isCysboyJe me doute que vous souhaiteriez
faire un retour à la ligne pour que votre texte soit plus lisible...

Pour réussir cela, vous avez plusieurs solutions.
- Soit vous utilisez un caractère d'échappement, ici, \n.
- Soit vous utilisez la méthode println() à la place de la méthode
print().
Donc, si
nous reprenons notre précédent code et que nous appliquons ceci, voilà ce que ça
donnerait :
(notre code modifié) :
Code : Java
1
2
3 |
System.out.print("Hello World ! ! \n");
System.out.println("My name is");
System.out.println("\nCysboy");
|
Le résultat
:
Hello World ! !
My name is
CysboyVous
pouvez voir que :
- lorsque vous utilisez le caractère d'échappement \n, quelle que soit la méthode appelée,
celle-ci intègre immédiatement un retour à la ligne à l'emplacement de ce
dernier.
- lorsque vous utilisez la méthode println(), celle-ci ajoute automatiquement un
retour à la ligne à la fin de la chaîne passée en paramètre.
- rien ne vous empêche de mettre un caractère d'échappement dans la méthode
println().
J'en profite au passage
pour vous donner deux autres caractères d'échappement :
- \r : va mettre un retour
chariot.
- \t : va faire une tabulation.
Vous avez sûrement remarqué que la chaîne de
caractères que l'on affiche est entourée de "
<chaine> ".
En Java, les double quotes sont
des délimiteurs de chaînes de caractères ! Si vous voulez afficher un double
quote avec la sortie standard, vous devrez "l'échapper" avec un \, ce qui donnerait
System.out.println("coucou mon
\"choux\" ! ");
Maintenant
que vous savez faire un "Hello World", je vous propose de voir la compilation de
vos programmes en ligne de commande.
Cette sous-partie n'est ici que pour les plus
curieux d'entre vous.
Vous pouvez passer outre cette sous-partie si vous le
voulez, et aller directement au QCM mais, partez du principe que ça pourrait
vous servir un jour...
Compilation en ligne de commande (Windows)
Donc bienvenue aux plus curieux !
Avant de vous apprendre à compiler et à exécuter un programme en ligne de
commandes, il va vous falloir le JDK de SUN (
Java SE
Development
Kit). C'est dans celui-ci que nous
aurons de quoi compiler nos programmes. Le nécessaire à l'exécution des
programmes est aussi dans le JRE... Mais vous l'aurez en plus dans le
JDK.
Je vous invite donc à retourner sur le site de SUN et à télécharger
celui-ci. Une fois cette opération effectuée, il est conseillé de mettre à jour
votre variable d'environnement %PATH%.
Euh... Quoi ?
Votre
variable
d'environnement. C'est grâce à celle-ci que Windows arrive à trouver
des exécutables sans lui spécifier le chemin d'accès complet. Vous, enfin
Windows, en a plusieurs, mais nous nous intéresserons qu'à une seule.
En gros, cette variable contient le chemin d'accès à certains programmes.
Par exemple, si vous spécifiez le chemin d'accès d'un programme X dans
votre variable d'environnement et que, comme par un malheureux hasard, vous
n'avez plus aucun raccourci vers X, c'est simple : vous l'avez définitivement
perdu dans les méandres de votre PC.
Eh bien vous pourrez le lancer en
faisant "
démarrer > Exécuter" et en tapant la commande
"
X.exe" (en partant du principe que
le nom de l'exécutable est
X.exe).
D'accord, mais comment on fait ? Et pourquoi on doit
faire ça pour le JDK ?
J'y arrive...
Une fois votre JDK installé, ouvrez le répertoire
bin de celui-ci,
mais également
celui-ci de votre JRE. Nous allons nous attarder sur deux fichiers.
Dans
le répertoire
bin de votre JRE, vous
devez avoir un fichier appelé
java.exe.
Fichier que vous retrouvez aussi dans le répertoire
bin de votre JDK. C'est grâce à ce fichier que votre
ordinateur peut lancer vos programmes par le biais de la JVM.
Le deuxième ne se trouve que dans le répertoire
bin de votre JDK, il s'agit de
javac.exe (
java compiler). C'est
celui-ci qui va pré-compiler vos programmes Java en byte code.
Alors
pourquoi le faire pour le JDK ?
Eh bien, compiler-exécuter en ligne de
commande revient à utiliser ces deux fichiers en leur précisant où se trouvent
les fichiers à traiter. Cela veut dire que si on ne met pas à jour la variable
d'environnement de Windows, nous devons :
- ouvrir l'invite de commande,
- se positionner dans le répertoire bin de notre JDK,
- appeler la commande souhaitée,
- préciser le chemin du fichier .java,
- renseigner le nom du fichier.
Avec notre variable
d'environnement mise à jour, nous n'aurons plus qu'à :
- nous positionner dans le dossier de notre programme,
- appeler la commande,
- renseigner le nom du fichier Java.
Comme un exemple vaut
mieux que de grands discours, mettons notre variable d'environnement à
jour...
Allez dans le "
panneau de configuration" de
votre PC, de là, cliquez sur l'icône "
Système" ; choisissez
l'onglet "
avancé" et vous devriez avoir un bouton, en bas,
intitulé "
Variables d'environnement" ; cliquez dessus.
Une
nouvelle fenêtre s'ouvre. Dans la partie inférieure intitulée "
Variables
système" chercher la variable
Path. Une fois
sélectionnée, cliquez sur
Modifier.
Encore une fois, une
fenêtre, plus petite celle-là, s'ouvre devant vous. Elle contient le
nom de la variable et sa
valeur.
Ne changez pas son nom et n'effacez pas le contenu de
valeur !
Nous allons juste ajouter un chemin
d'accès.
Pour ce faire, allez jusqu'au bout de la
valeur de la variable, ajoutez-y un point-virgule (
;) s'il n'y
en a pas, et ajoutez maintenant le chemin d'accès au répertoire
bin de votre JDK, en terminant celui-ci par un
point-virgule !.
Chez moi, ça donne ceci :
"
C:\Sun\SDK\jdk\bin".
Alors, ma
variable d'environnement contenait avant ajout :
%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;Et maintenant :
%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;C:\Sun\SDK\jdk\bin;Validez les changements, et c'est tout !
Vous êtes maintenant prêts à compiler en ligne de commande.
Pour bien
faire, allez dans le répertoire de votre premier programme et effacez le
.class.
Ensuite, faites
"
démarrer > Exécuter" (ou encore touche Windows + r), et
tapez "
cmd".
Pour rappel, dans l'invite de commande, on se
déplace de dossier en dossier grâce à l'instruction cd.
cd <nom du dossier enfant> : pour aller
dans un dossier contenu dans celui dans lequel nous sommes.
cd .. : pour remonter d'un dossier dans la
hiérarchie.
Par exemple : lorsque j'ouvre la console, je me
trouve dans le dossier
C:\toto\titi et mon application
se trouve dans le dossier
C:\sdz, je fais donc
:
Code : Bash
Lors de la première instruction,
je me retrouve dans le dossier
C:\toto.
Lors de la
deuxième instruction, je me retrouve à la racine de mon disque.
Lors de la
troisième instruction, je me retrouve dans le dossier
C:\sdz.
Nous sommes maintenant dans le dossier contenant
notre fichier Java !
Mais nous pouvons condenser ceci en :
Code :
Bash
Maintenant, vous pouvez
créer votre fichier
.class en utilisant
la commande
javac
<nomDeFichier.java>.
Si, dans mon dossier, j'ai
un fichier
test.java, je le compile en faisant
:
javac
test.javaEt si vous n'avez aucun message d'erreur,
vous pouvez vérifier que le fichier
test.class est
présent en utilisant l'instruction
dir qui liste le contenu d'un
répertoire.
Cette étape réussie, vous pouvez lancer votre programme Java
en faisant
java
<nomFichierClassSansExtension>Ce qui nous donne
:
java
testEt normalement, vous avez le résultat de votre
programme Java qui s'affiche sous vos yeux ébahis !
Attention : il ne faut pas mettre l'extension du
fichier pour le lancer, mais la mettre pour le compiler.
Donc voilà : vous avez compilé et exécuté un programme
Java en ligne de commande... Vous en aurez peut être besoin un jour... En tout
cas, j'espère que vous êtes d'attaque pour un petit QCM...
J'espère que maintenant vous arrivez mieux à cerner
comment fonctionne Java, et à écrire ce que vous voulez à l'écran.
À présent,
nous allons voir comment stocker des données en mémoire afin de les afficher, de
les calculer...
En avant pour les variables
et les opérateurs.
Les variables et les opérateurs
Nous y voilà. Encore un chapitre barbant... Mais celui-là
aussi est nécessaire, et je dirais même vital !
En Java, comme dans beaucoup
de langages de programmation, avant d'utiliser une variable, nous devons d'abord
définir ce qu'elle va contenir.
T'es bien gentil, mais c'est quoi, une variable ?
Oups ! Désolé ! Je parle, je parle
et j'oublie l'essentiel... Une variable, c'est ce qui va nous permettre de
stocker des informations de toute sorte (chiffres, résultats de calcul, des
tableaux, des renseignements fournis par l'utilisateur...). Bref, vous ne
pourrez pas faire de programme sans variables. Et comme je viens de vous le
dire,
et j'insiste là dessus, il faut à tout prix définir ce
que nos variables vont contenir avant de les utiliser.
Une déclaration de variable se fait comme suit
:
<Type de la variable> <Nom de la variable> ;
Cette
opération se termine toujours par un ";" (comme toutes les instructions de ce
langage) ;
ensuite, on l'initialise en rentrant une
valeur.
Bon. Assez de bla-bla, on y va. (Décidément, moi, les rimes
en ce moment...

)
Les différents types de variables
Tout d'abord, je vous conseille vivement de lire
http://www.siteduzero.com/tuto-3-1224-1-un-monde-de-variables.html#ss_part_1
dans son tutoriel sur le C. Je pense que vous y verrez plus clair.
Bon.
En Java, nous avons deux type de variables :
- des variables de type simple ou "primitif",
- des variables de type complexe ou encore des objets.
Ce qu'on
appelle des types simples, ou types primitifs, en Java ce sont tout bonnement
des nombres entiers, des nombres réels, des booléens ou encore des caractères.
Mais vous allez voir qu'il y a plusieurs façons de déclarer certains de ces
types.
Commençons par les variables de type
numérique
- Le type
byte (1 octet) peut contenir les entiers
entre -128 et +127.
Ex. :
Code : Java
1
2 |
byte temperature;
temperature = 64;
|
- Le type
short (2
octets) contient les entiers compris entre -32768 et +32767.
Ex. :
Code : Java
1
2 |
short vitesseMax;
vitesseMax = 32000;
|
- Le type
int (4 octet)
va de -2*10e9 à 2*10e8 (2 et 9 zéros derrière... ce qui fait déjà un joli
nombre).
Ex. :
Code : Java
1
2 |
int temperatureSoleil;
temperatureSoleil = 15600000;
|
C'est en degrés Kelvin...
-
Le type
long(8 octets) de -9*10e18 à 9*10e18
(encore plus gros...).
Ex. :
Code : Java
1
2 |
long anneeLumiere;
anneeLumiere = 9460700000000000;
|
- Le type
float (4
octets) correspond à des nombres avec virgule flottante.
Ex. :
Code : Java
1
2 |
float pi;
pi = 3.141592653;
|
ou encore
Code : Java
1
2 |
float nombre;
nombre = 2.0;
|
Vous remarquerez que nous ne mettons pas de virgule
mais un point ! Et vous remarquerez aussi que même si le nombre
en question est rond, on met tout de même .0 derrière celui-ci !
- Le
type
double (8 octets) est identique à
float, si
ce n'est qu'il contient un nombre plus grand derrière la virgule.
Ex.
:
Code : Java
1
2 |
double division;
division = 0.333333333333333333333333333333333333333333334;
|
Nous avons aussi des variables stockant du
caractère
- Le type
char contient
UN
caractère stocké entre de simples quotes
' '
comme ceci...
Ex. :
Code : Java
1
2 |
char caractere;
caractere = 'A';
|
Nous avons aussi le type booléen
- Le
type
boolean qui lui contient
true (vrai) ou
false
(faux).
Ex. :
Code : Java
1
2 |
boolean question;
question = true;
|
Et aussi le type String
- Celle-ci
correspond à de la chaîne de caractères.
Ici, il ne s'agit pas d'une variable
mais d'un objet qui instancie une classe qui existe dans Java ; nous pouvons
l'initialiser en utilisant l'opérateur unaire
new() dont on se sert pour
réserver un emplacement mémoire à un objet (mais nous reparlerons de tout ceci
dans la partie deux, lorsque nous verrons les classes), ou alors lui affecter
directement la chaîne de caractères.
Vous verrez que celle-ci s'utilise très
facilement et se déclare comme ceci :
Ex. :
Code :
Java
1
2
3
4
5
6
7
8
9 |
String phrase;
phrase = "Titi et gros minet";
//Deuxième méthode de déclaration de type String
String str = new String();
str = "Une autre chaîne de caractères";
//La troisième
String string = "Une autre chaîne";
//Et une quatrième pour la route
String chaine = new String("Et une de plus ! ");
|
Attention : String commence
par une majuscule ! Et lors de l'initialisation, on utilise ici des doubles
quotes " " .
En fait, String n'est pas un type de variable mais un
objet.
Notre variable est
"un objet", on dit aussi
"une instance", ici,
une
instance de la classe String. Nous y reviendrons lorsque
nous aborderons les objets.
On te croit sur parole, mais pourquoi String commence
par une majuscule et pas les autres ?
C'est simple. Il s'agit d'une
convention de nommage.
En fait, c'est une façon d'appeler nos classes, nos
variables, etc. Il faut que vous essayez de respecter cette convention au
maximum. Cette convention, la voici :
- tous vos noms de classes doivent commencer par une majuscule !
- tous vos noms de variables doivent commencer par une minuscule.
- Si un nom de variable est un nom composé, celui-ci commence par une
minuscule et son composé par une majuscule. Et ce, sans séparation.
- Tout ceci sans accent ! !
Je sais que la première classe que je vous ai demandé de créer
ne respecte pas cette convention, mais je ne voulais pas vous dire ça à ce
moment-là... Donc, à présent, je vous demanderai de ne pas oublier ces règles !
Sinon !

Châtiment corporel.
Voici quelques exemples de noms de classes et de variables :
Code : Java
1
2
3
4
5
6
7
8 |
public class Toto{}
public class Nombre{}
public class TotoEtTiti{}
String chaine;
String chaineDeCaracteres;
int nombre;
int nombrePlusGrand;
//...
|
Donc, pour en revenir au
pourquoi du comment, je vous ai dit que les variables de type String sont des
objets. Les objets sont définis par une ossature (un squelette) qui est en fait
une classe. Ici, nous utilisons un objet String qui est défini dans une classe
qui s'appelle "
String" ; c'est
pourquoi String à une majucule et pas int, float... qui eux ne sont pas définis
par une classe.
Pfiou ! Ça en fait des choses à retenir d'un coup ! Mais
je ne vous cache pas que les deux premiers types de variables ne sont pas trop
utilisés...
Chose importante
: veillez à bien respecter la casse (majuscules et minuscules)
car une déclaration de CHAR à la place de char ou autre chose provoquera une
erreur, tout comme une variable de type string à la place de String
!
Alors faites bien attention lors de vos déclarations de variables...
Sinon une petite astuce quand même, enfin deux plutôt ! On peut très bien
compacter la phase de déclaration et d'initialisation en une seule phase ! Comme
ceci :
Code : Java
1
2
3
4 |
int entier = 32;
float pi = 3.1416;
char carac = 'z';
String mot = new String("Coucou");
|
Et lorsque nous avons plusieurs
variables d'un même type, nous pouvons compacter tout ceci en une déclaration
comme ceci :
Code : Java
1 |
int nbre1 = 2, nbre2 = 3, nbre3 = 0;
|
Ici, toutes les variables sont
des entiers et toutes initialisées.
Bon, t'es bien mignon, mais on code quand, nous
?
Minute papillon ! On va y arriver !
Mais avant, nous avons encore quelque chose de très important à voir pour
pouvoir travailler sur et avec nos variables :
Les opérateurs arithmétiques
Les opérateurs arithmétiques
Ceci est notre avant dernière ligne droite avant de
commencer à coder ! Et après, vous regretterez le temps où vous n'aviez pas à
vous creuser la tête !!
Bon, allez ! Je vois bien que vous brûlez d'impatience, alors on va y
aller tout de suite.
Les opérateurs arithmétiques
Ce sont ceux que l'on apprend à
l'école primaire...
+
permet d'ajouter deux variables numériques (mais aussi de concaténer des chaînes
de caractères ! Ne vous inquiétez pas, on aura l'occasion d'y
revenir).
- permet de
soustraire deux variables numériques.
* permet de multiplier deux variables
numériques.
/ Permet de
diviser deux variables numériques.(mais je crois que vous aviez deviné

).
% permet de renvoyer le
reste de la division de deux variables de type numériques, cet opérateur s'
appel le
modulo.
Quelques exemples de calcul
Code : Java
1
2
3
4
5
6
7
8
9 |
int nbre1, nbre2, nbre3;//déclaration des variables</couleur>
nbre1 = nbre2 = nbre3 = 0;//initialisation</couleur>
nbre1 = 1 + 3; // ici nbre1 vaut 4
nbre2 = 2 * 6; // ici nbre2 vaut 12
nbre3 = nbre2 / nbre1; //ici nbre3 vaut 3
nbre1 = 5 % 2; //ici nbre1 vaut 1 car 5 = 2 * 2 + 1
nbre2 = 99 % 8; //ici nbre2 vaut 3 car 99 = 8 * 12 + 3
nbre3 = 6 % 3; //et là, nbre3 vaut 0 car la division n'a aucun reste
|
Ici, nous voyons bien que nous
pouvons affecter des opérations sur des nombres à nos variables mais aussi
affecter des opérations sur des variables de même type !!
Maintenant, quelque chose que les personnes qui n'ont jamais
programmé ont du mal à assimiler. Je garde la même déclaration de variable que
ci-dessus.
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
int nbre1, nbre2, nbre3; //déclaration des variables
nbre1 = nbre2 = nbre3 = 0; //initialisation
nbre1 = nbre1 + 1; //nbre1 = lui même donc 0 + 1 => nbre1 = 1
nbre1 = nbre1 + 1; // nbre1 = 1 (cf ci-dessus) maintenant nbre1 = 1 + 1 = 2!!!
nbre2 = nbre1; //nbre2 = nbre1 = 2
nbre2 = nbre2 * 2; //nbre2 = 2 => nbre2 = 2 * 2 = 4
nbre3 = nbre2; //nbre3 = nbre2 = 4
nbre3 = nbre3 / nbre3; //nbre3 = 4 / 4 = 1
nbre1 = nbre3; //nbre1 = nbre3 = 1
nbre1 = nbre1 - 1;// nbre1 = 1 - 1 = 0
|
Et là aussi, il existe une
syntaxe qui raccourcit l'écriture de ce genre d'opération. Regardez bien
:
Code : Java
1
2
3
4 |
nbre1 = nbre1 + 1;
nbre1 += 1;
nbre1++;
++nbre1;
|
Ces trois syntaxes correspondent
exactement à la même opération. La troisième syntaxe sera certainement celle que
vous utiliserez le plus...
mais ne marche que pour augmenter
la valeur de 1 ! Si vous devez augmenter la valeur d'une variable de 2, utilisez
les deux syntaxes précédentes. On appelle cette syntaxe
l'incrémentation. La dernière fait la même chose que la
troisième, mais avec une subtilité en plus... Nous en reparlerons dans le
chapitre sur les boucles.
Sinon, la syntaxe est identique pour la
soustraction :
Code : Java
1
2
3
4 |
nbre1 = nbre1 - 1;
nbre1 -= 1;
nbre1--;
--nbre1;
|
Même commentaire que pour
l'addition sauf qu'ici, la troisième syntaxe s'appelle la
décrémentation.
Les raccourcis pour
la multiplication marchent aussi ; regardez plutôt :
Code :
Java
1
2
3
4 |
nbre1 = nbre1 * 2;
nbre1 *= 2;
nbre1 = nbre1 / 2;
nbre1 /= 2;
|
TRES IMPORTANT == On ne peut faire de traitement
arithmétique que sur des variables de même type, sous peine de perdre de la
précision lors du calcul. On ne s'amuse pas à diviser un int
par un float ! Ou pire, par un char !! Et ceci
est valable pour tous les opérateurs arithmétiques et pour tous les types de
variables numériques. Essayer de garder une certaine rigueur pour vos calculs
arithmétiques.
Voici les raisons de ma mise en garde.
Comme je
vous l'ai dit plus haut, chaque type de variable a une contenance différente et,
pour faire simple, nous allons comparer nos variables à différents
récipients.
Une variable de type :
- byte correspondrait à un dé à coudre. On ne
met pas beaucoup de chose dedans...
- int, à un verre. C'est déjà plus grand.
- double, à un baril. Pfiou, on en met
là-dedans...
A partir de là, ce n'est plus que du bon sens. Vous devez
facilement voir que vous pouvez mettre le contenu d'un dé à coudre dans un verre
ou un baril. Mais par contre, si vous versez le contenu d'un baril dans un
verre... Il y en a plein par terre !
Cela s'appelle :
une perte de précision !Ainsi, si
nous affectons le résultat d' une opération sur deux variables de type
double dans une variable de type
int, le résultat sera de type
int et
donc ne sera pas un réel mais un
entier.
Pour afficher le contenu d'une variable dans la
console, il vous suffit d'appeler l'instruction : System.out.println(maVariable);, ou encore
System.out.print(maDeuxiemeVariable);.
Je
suppose que vous voudriez aussi mettre du texte en même temps que vos
variables... Eh bien sachez que l'opérateur
+ sert aussi comme opérateur de concaténation,
c'est-à-dire qu'il permet de mixer du texte brut avec des
variables.
Voici un exemple d'affichage avec une
perte de précision :
Code : Java
1
2
3 |
double nbre1 = 10, nbre2 = 3;
int resultat = (int)(nbre1 / nbre2);
System.out.println("Le résultat est = " + resultat);
|
Mais pour le bien de ce chapitre, nous n'allons pas utiliser cette
méthode...
Vous devez voir que le résultat fait 3, au lieu de
3.33333333333333... Et je pense que vous êtes intrigués par ça :
int resultat = (int)(nbre1 /
nbre2);Avant de vous expliquer, remplacez la ligne citée
ci-dessus par celle-ci :
int
resultat = nbre1 / nbre2;Vous devez voir
qu'Eclipse n'aime pas du tout !
Pour savoir pourquoi, nous allons voir ce
qu'on appelle
les conversions ou
"cast".
Les conversions, ou "cast"
Comme expliqué plus haut, les variables de type
double contiennent plus d'informations que les variables
de type
int.
Ici, il va falloir écouter comme
il faut... heu, pardon ! Lire comme il faut !
Nous allons voir un truc super
important en Java. Ne vous en déplaise, vous serez amenés à convertir des
variables...
Conversion de type int vers un autre type
numériqueD'un type
int en type
float :
Code :
Java
1
2 |
int i = 123;
float j = (float)i;
|
D'un type
int en
double :
Code :
Java
1
2 |
int i = 123;
double j = (double)i;
|
Et inversement :
Code : Java
1
2
3
4 |
double i = 1.23;
double j = 2.9999999;
int k = (int)i; // k vaut 1
k = (int)j; // k vaut 2
|
Ce type de conversion
s'appelle une
conversion d'ajustement ou
cast
de variable.
Vous l'avez vu : nous pouvons passer directement d'un type
int à un type
double.
Mais ceci ne
fonctionnera pas dans le sens inverse sans une perte de précision.En
effet comme vous avez pu le constater, lorsque nous castons un
double en
int, la valeur de ce
double est
tronqué. Ce qui signifie
que l'
int en question prendra la valeur entière du
double quelle que soit la
valeur des décimales.
Pour en revenir à notre ancien
problème, il est aussi possible de caster le résultat d'une opération
mathématique. En mettant celle-ci entre
() et précédée du type
de cast souhaité.
Donc :
Code : Java
1
2
3 |
double nbre1 = 10, nbre2 = 3;
int resultat = (int)(nbre1 / nbre2);
System.out.println("Le résultat est = " + resultat);
|
Fonctionne parfaitement.
Mais pour un résultat optimal, vous devez mettre le résultat de l'opération en
type
double.
Et si on faisait l'inverse ? Si nous déclarons deux
entiers et que nous mettons le résultat dans un double ?
Comme ceci ?
Code : Java
1
2
3 |
int nbre1 = 3, nbre2 = 2;
double resultat = nbre1 / nbre2;
System.out.println("Le résultat est = " + resultat);
|
Vous auriez
1 comme résultat.
Je ne cast pas ici, car un
double peu contenir
un
int.
Et comme ça ?
Code :
Java
1
2
3 |
int nbre1 = 3, nbre2 = 2;
double resultat = (double)(nbre1 / nbre2);
System.out.println("Le résultat est = " + resultat);
|
Idem...
Comment doit-on faire, alors ?
Avant de vous
donner la réponse, vous devez savoir qu'en Java, comme dans d'autres langages
d'ailleurs, il y a la notion de
priorité
d'opération et là, nous en avons un très bon exemple !
Sachez que l'affectation, le calcul, le
cast, le test, l'incrémentation... toutes ces choses sont des opérations
!
Et Java les fait dans un certain ordre, suivant une
priorité.
Dans le cas qui nous intéresse, il y a trois opérations :
- un calcul,
- un cast de valeur de résultat de calcul,
- une affectation dans la variable resultat.
Eh bien Java exécute cette ligne dans cet ordre !
Il fait le calcul (ici 3/2), il cast le résultat en
double, puis il l'affecte dans notre variable
resultat.
D'accord, mais pourquoi on a pas 1.5, alors
?
C'est simple : lors de la première opération de Java, la JVM voit un
cast à effectuer mais sur un résultat de calcul. La JVM fait ce calcul (division
de deux
int qui, ici, nous donne 1), puis le cast
(toujours 1) et affecte la valeur à la variable (encore et toujours 1).
Donc,
pour avoir un résultat correct, il faudrait caster chaque nombre avant de faire
l'opération, comme ceci :
Code : Java
1
2
3 |
int nbre1 = 3, nbre2 = 2;
double resultat = (double)(nbre1) / (double)(nbre2);
System.out.println("Le résultat est = " + resultat); //affiche: Le résultat est = 1.5
|
Message reçu mais... peut-on changer un type numérique
en autre chose ?
Bien sûr, je ne détaillerai pas trop ceci mais
maintenant, vous allez transformer l'argument d'un type donné,
int par exemple, en
String.
Voici la méthode à
appliquerCode : Java
1
2
3 |
int i = 12;
String j = new String();
j = j.valueOf(i);
|
j est donc une variable de type
String contenant la
chaîne de
caractères 12.
Ceci fonctionne aussi
avec les autres types numériques. Voyons maintenant comment
faire marche arrière, en partant de ce que nous venons de faire.
Code : Java
1
2
3
4
5
6 |
int i = 12;
String j = new String();
j = j.valueOf(i);
int k = Integer.valueOf(j).intValue();
|
Maintenant, la variable
k est de type
int, et contient le nombre
12.
Il y a l'équivalent de intValue() pour les autres types numérique : floatValue(), doubleValue()...
Astuce d'Eclipse
Retapez le code qu'Eclipse n'aimait pas ; pour mémoire,
c'était celui-ci :
Code : Java
1
2
3 |
double nbre1 = 10, nbre2 = 3;
int resultat = nbre1 / nbre2;
System.out.println("Le résultat est = " + resultat);
|
Eclipse vous souligne
nbre1 / nbre2 et vous met une croix rouge sur la gauche de
la zone d'édition, sur cette même ligne.
Si
vous cliquez sur cette croix rouge, vous aurez ceci :
Double-cliquez
sur
"Add cast to 'int'" et Eclipse vous met automatiquement le
cast de l'opération !

Ensuite
pour tester, vous pouvez cliquez sur
"Run" ou appuyer sur
Control + F11.
Si vous faites cette manipulation (Ctrl +
F11), une fenêtre s'ouvre et vous demande quelle ressource sauver, puis lancer.
Pour le moment, le choix est simple puisque vous n'avez qu'une classe. Vous
n'avez plus qu'à valider et votre programme se lance !
Au fil de ce tutoriel, vous verrez que cette
manipulation va beaucoup vous aider pour des erreurs en tous genres !
Je suis bien conscient que ces deux chapitres étaient
riches en vocabulaire, en nouveautés (pour ceux qui n'auraient pas lu les cours
de M@teo), mais bon, voilà : votre calvaire... ne fait que commencer.
Eh oui : tout ceci était un amuse-bouche ! Maintenant, nous rentrons dans le
vif du sujet. Dans tous les cas, si vous n' avez pas eu 20/20 à ces deux
questionnaires, relisez ces deux chapitres !!
Allez on s' y jette :
Lire les entrées
clavier ! TAAAAÏÏÏÏAAAAUUUUTTTT !
Lire les entrées clavier
Dans ce chapitre, nous verrons comment lire les entrées
clavier.
Nous survolerons ceci sans voir les différents cas d'erreurs que
cela peut engendrer.
Trêve de bavardage, je suis sûr que vous êtes
impatients...
Utilisation de l'objet Scanner
Je me doute qu'il vous tardait de pouvoir communiquer
avec votre application...
Le moment est enfin venu ! Mais je vous préviens, la méthode que je vais
vous donner à des failles.
Je vous fais confiance pour ne pas rentrer
n'importe quoi n'importe quand...
Pour les puristes qui me diront "
ouais, mais si je rentre ça, tu vas te prendre une belle
exception dans ta face !", je le sais, mais je ne trouve pas opportun de
vous parler des exceptions et de la manière de les capturer maintenant... Bon.
Allons-y !
Je vous ai dit que vos variables de type
String sont en fait des
objets de type
String. Pour que Java puisse lire ce que vous
tapez au clavier, vous allez devoir utiliser un objet de type
Scanner (merci à Myhtrys).
Je vous rappelle que pour
instancier un objet, c'est-à-dire créer un nouvel objet afin de
s'en servir, vous devez utiliser l'opérateur unaire new().
Cet objet peut prendre
différents paramètres, mais ici, nous n'en utiliserons qu'un. Celui qui
correspond justement à l'entrée standard en Java.
Lorsque vous faites
System.out.println();, je vous
rappelle que vous appliquez la méthode
println() sur
la sortie standard ; or ici, nous allons utiliser l'entrée standard
System.in.
Donc, avant de dire à Java
de lire ce que nous allons taper au clavier, nous devrons instancier un objet
Scanner.
Pour pouvoir utiliser un objet Scanner, nous devons dire à Java où trouver cet objet
!
Avant de vous expliquer ceci, créez une nouvelle classe et tapez
cette ligne de code dans votre méthode main :
Code :
Java
1 |
Scanner sc = new Scanner(System.in);
|
Vous devez avoir une zolie vague
rouge sous le mot
Scanner.
Mais
souvenez-vous de l'astuce que je vous avais donnée pour le cast de variables.
Cliquez sur la croix rouge sur la gauche et double-cliquez sur "
Import 'Scanner' java.util". Et là, l'erreur
disparaît !
Maintenant,
regardez tout au dessus de votre classe, au dessus de la déclaration de
celle-ci, vous devez avoir une ligne :
Code : Java
1 |
import java.util.Scanner;
|
Voilà ce que nous avons
fait. Je vous ai dit qu'il fallait dire à Java où se trouve la classe
Scanner.
Pour faire ceci, nous devons
importer la classe Scanner, grâce à l'instruction
import.
La classe que nous
voulons se trouve dans le
package java.util.
Je pense que vous avez
compris qu'un package est en fait un ensemble de classes. En fait, c'est un
ensemble de dossiers et de sous-dossiers contenant une ou plusieurs
classes.
Par exemple, nous avons importé tout à l'heure la classe
Scanner qui se trouve dans le package
java.util. Remplacez les
. par des
/,
l'arborescence des dossiers est donc
java/util/ et dans ce dossier se trouve le fichier
Scanner.class ! Vous verrez ceci plus
en détail lorsque nous ferons nos propre packages.
Les classes qui se trouvent dans les autres packages que
java.lang sont à importer
à la
main dans vos classes Java pour pouvoir vous en servir.
La façon dont
nous avons importé la classe
java.util.Scanner dans
Eclipse est très pratique et très simple. Vous pouvez aussi le faire
manuellement en tapant :
Code : Java
1
2
3
4 |
//Ceci importe la classe Scanner du package java.util
import java.util.Scanner;
//Ceci importe toutes les classes du package java.util
import java.util.*;
|
Si vous faites vos imports manuellement, n'oubliez
surtout pas le ; à la fin
!
Maintenant que vous avez assimilé ceci, nous pouvons retourner à nos
moutons !
Récupérez ce que vous tapez
Retournons à notre morceau de code, pour mémoire
:
Code : Java
1 |
Scanner sc = new Scanner(System.in);
|
Afin de vous préparer à
saisir des informations, veuillez afficher un message à l'écran ; nous avons
donc :
Code : Java
1
2 |
Scanner sc = new Scanner(System.in);
System.out.println("Veuillez saisir un mot :");
|
Maintenant, voici l'instruction
pour permettre à Java de récupérer ce que vous avez saisi et ensuite de
l'afficher :
Code : Java
1
2
3
4 |
Scanner sc = new Scanner(System.in);
System.out.println("Veuillez saisir un mot :");
String str = sc.nextLine();
System.out.println("Vous avez saisi : " + str);
|
Une fois l'application lancée, le message que vous
avez écrit auparavant s'affiche dans la console, en bas dans Eclipse (vous avez
l'habitude, maintenant

).
Pensez à cliquer dans la console, après votre
message, afin que ce que vous saisissez soit écrit dans la console, pour que
Java puisse récupérer ce que vous avez inscrit !
Alors
? Heureux ?
Voilà votre première saisie clavier ! Comme vous avez pu le constater,
l'instruction
nextLine() renvoie une chaîne de
caractères. Si vous avez essayé de remplacer la variable de type
String par une variable d'un autre type, vous avez dû voir
qu'Eclipse n'apprécie pas du tout... Et si vous avez cliqué sur la croix rouge
pour corriger le problème, vous constatez que la seule solution qu'il vous
propose est de changer le type de votre variable pour le type String.
Vous aurez remarqué aussi qu'Eclipse vous simplifie
la vie au maximum. Lorsque vous tapez "sc.", Eclipse vous propose une liste de méthodes
appartenant à cet objet.
Si vous remplacez la ligne de code qui
récupère une chaîne de caractères comme suit :
Code :
Java
1
2
3
4 |
Scanner sc = new Scanner(System.in);
System.out.println("Veuillez saisir un nombre :");
int str = sc.nextInt();
System.out.println("Vous avez saisi le nombre : " + str);
|
vous devez voir que lorsque vous
utilisez votre variable de type Scanner, et où vous tapez le point permettant
d'appeler des méthodes de l'objet, Eclipse vous donne une liste de méthodes
associées à cet objet et, lorsque vous commencez à taper le début de la méthode
nextInt(), le choix se restreint
jusqu'à ne laisser que cette seule méthode.
Exécutez et testez ce
programme et vous verrez qu'il fonctionne à la perfection !
Sauf... si
vous saisissez autre chose qu'un entier !
C'est ce que je vous disais au départ de ce chapitre.
L'objet essaie de récupérer un entier, mais si vous lui donnez autre chose, une
exception est levée ! Nous verrons ceci lorsque nous programmerons des
objets...
Pour les curieux, voici ce que donnerait l'erreur :
Vous
savez maintenant que pour lire un
int, vous devez
utiliser
nextInt().
De façon
générale, dites-vous que pour récupérer une type de variable, il vous suffit
d'appeler
next<Type de variable commençant par une
majuscule> (rappelez-vous de la convention de nommage !).
Bon.
C'est mon jour de bonté :
Code : Java
1
2
3
4
5
6 |
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
double d = sc.nextDouble();
long l = sc.nextLong();
byte b = sc.nextByte();
//etc
|
Attention :
il y a un type de variable primitive qui n'est pas pris en compte par la classe
Scanner ; il s'agit des variables de type
char.
Voici comment on
pourrait récupérer un caractère :
Code : Java
1
2
3
4
5 |
System.out.println("Saisissez une lettre :");
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
char carac = str.charAt(0);
System.out.println("Vous avez saisi le caractère : " + carac);
|
Qu'est-ce que nous avons fait ici ?Nous avons
récupéré une chaîne de caractères, puis nous avons utilisé une méthode de
l'objet
String (ici,
charAt(0) ) afin de récupérer le premier
caractère saisi !
Même si vous tapez une longue chaîne de caractères,
l'instruction
charAt(0) ne renverra
que le premier caractère...
Vous devez vous demander pourquoi charAt(0) et non charAt(1) ?
Ne vous inquiétez pas, nous aborderons ce point lorsque nous verrons les
tableaux...
Jusqu'à ce qu'on aborde les exceptions, je vous
demande d'être rigoureux et de faire attention à ce que vous attendez comme
donnée afin d'utiliser la bonne méthode.
Voilà : je pense que vous êtes
fin prêts pour un QCM, maintenant !
J'espère que cette partie vous a plu et que vous êtes
encore en forme...
A partir de maintenant, nous allons aborder les
différentes instructions du langage.
Commençons par les conditions.
Les conditions
Nous abordons ici un des chapitres les plus importants et les
plus utilisés.
Vous allez voir que tous vos projets ne sont que des
enchaînements et des imbrications de
conditions et de boucles (partie
suivante).
Dans une classe, la lecture et l'exécution se font de façon séquentielle.
C'est-à-dire en suivant, ligne par ligne. Avec les
conditions,
nous allons pouvoir gérer différents cas de figure, sans pour autant lire tout
le code.
Assez de belles paroles ! Rentrons tout de suite dans le vif du
sujet.
Les opérateurs logiques
Ceux-ci sont surtout utilisés lors de conditions
(SI****** alors fait ceci*****) pour tester des vérités ou des contrevérités !
Mais nous verrons plus en détails leur utilité dans un autre chapitre ! Je vous
les colle ici car ils sont faciles à mémoriser ; et puis comme ça, c'est fait,
on n'en parlera plus.
== permet de tester l'égalité. Prenons l'exemple
complètement inutile suivant (en français, pour le code Java, ce sera plus
tard...).
SI bleu == bleu alors fait ceci.......
!= pas égal ou encore différent de.
Je pense que c'est assez parlant, non ?
< strictement inférieur.
<= inférieur ou égal. Vous
l'aviez déjà trouvé, je suis sûr ! ! !
> strictement supérieur.
>= eh oui, voici son grand frère,
le majestueux supérieur ou égal !
&& voici l'opérateur ET. Permet de préciser une
condition.
|| est le
cousin du ET, le OU. Même combat que le précédent.
? : L'opérateur ternaire, pour
celui-ci vous comprendrez mieux avec un exemple... vers la fin de ce chapitre.
Comme je vous l'ai dit dans la partie précédente,
les opérations en Java sont soumises à un ordre de priorité. Tous ces opérateurs
sont soumis à cette règle, de la même manière que les opérateurs
arithmétiques...
Il n'y a pas grand-chose à dire sur ces opérateurs
sans un exemple concret, donc allons-y.
La structure if....else
Pour commencer, je vais vous expliquer à quoi servent
ces structures conditionnelles. Elles servent tout simplement à pouvoir
constituer un programme, en examinant les différents cas de figure que celui-ci
propose.
Je m'explique : imaginons un programme qui demande à un utilisateur
de rentrer un nombre réel (qui peut être soit négatif, soit nul, soit positif).
Les structures conditionnelles vont nous permettre de gérer ces trois cas de
figure.
La structure de ces conditions ressemble à ça :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12 |
if(//condition)
{
.....// exécution des instructions si la condition est remplie
.....
.....
}
else
{
....// exécution des instructions si la condition n'est pas remplie
....
....
}
|
Ceci peut se traduire par
"SI... SINON...".
Mettons notre petit exemple du dessus en pratique
:
Code : Java
1
2
3
4
5
6
7 |
int i = 10;
if (i < 0)
System.out.println("Le nombre est négatif");
else
System.out.println("Le nombre est positif");
|
Testez ce petit code, et
vous verrez comment il fonctionne (par exemple, avec la fonction de pas à
pas).
Dans ce cas, notre classe affiche que "
le
nombre est positif".
Expliquons un peu ce qui se passe :
- dans un premier temps, la condition du if est
testée... (qui dit SI i est strictement inférieur
à 0).
- Vu que celle-ci est fausse, le programme exécute le else.
Attends un peu ! Lorsque tu nous a présenté la
structure des conditions, tu as mis des accolades et là, tu n'en mets
pas...
Bien observé. En fait, les accolades sont la
structure "normale" des conditions mais, lorsque le
code à l'intérieur d'une d'entre elles n'est composé que d'une seule
ligne de code, les accolades deviennent
facultatives.
Comme nous avons l'esprit perfectionniste,
nous voulons que notre programme affiche "
le nombre est nul",
lorsque
i est égal à 0 ; nous allons donc rajouter
une condition.
Comment faire... La condition du
if est remplie si le nombre est strictement négatif, ce
qui n'est pas le cas ici puisque nous allons le mettre à 0, le code contenu dans
la clause
else est donc exécuté si le nombre est
égal à 0 et strictement supérieur à 0.
Il nous suffit de rajouter une
condition à l'intérieur de la clause
else. Comme
ceci :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13 |
int i = 0;
if (i < 0)
System.out.println("Ce nombre est négatif !");
else
{
if(i == 0)
System.out.println("Ce nombre est nul !!");
else
System.out.println("Ce nombre est positif !!");
}
|
Ici, la seule petite chose
qui devrait vous interpeler, c'est l'
imbrication d'une
structure
if... else dans un else. Et encore, parce que je suis tolérant...
Vous voyez aussi que le code à l'intérieur de la première clause
if ne contient qu'une seule ligne
de code
[=> accolades facultatives] et que la clause
else correspondante, elle, a
plusieurs ligne en son sein
[=> on entoure donc le code de cette
dernière avec des accolades]. Les clauses à l'intérieur du premier
else n'ont, elles aussi, qu'une
seule ligne de code
[=> accolades facultatives].Vous
verrez vite que vos programmes ne seront que des imbrications de
conditions.
Je vous fais voir une autre manière de le faire
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
int i = 0;
if (i <= 0)
{
if(i == 0)
System.out.println("Ce nombre est nul !!");
else
System.out.println("Ce nombre est négatif !");
}
else
System.out.println("Ce nombre est positif !!");
|
Je pense que ce code parle de
lui-même...
Ici, la clause du
if est remplie si
i est
INFÉRIEUR OU ÉGAL à 0,
i vaut 0 la condition est remplie. La suite, vous la
connaissez.
Maintenant que vous avez tout compris, je vais vous montrer une autre
façon de faire ce code, avec le même résultat (encore heureux !

). En ajoutant juste un petit
SINON
SI. Regardez bien la magie.
Code : Java
1
2
3
4
5
6
7
8
9 |
int i = 0;
if (i < 0)
System.out.println("Ce nombre est négatif !");
else if(i > 0)
System.out.println("Ce nombre est positif !!");
else
System.out.println("Ce nombre est nul !!");
|
Alors ? Explicite, n'est-ce
pas ?
- SI i est strictement négatif
=> exécution du code.
- SINON SI i est positif =>
exécution du code.
- SINON i est forcément nul
=> exécution du code.
Il faut absolument donner au
else if une condition pour qu'il fonctionne. Sans
cela, Eclipse vous mettra de zolies vagues rouges sous votre
else if.
Vous en avez sûrement déjà vu à de
nombreuses reprises...
Par contre, je vais TRÈS FORTEMENT INSISTER sur un point
: regardez l'affichage du code : remarquez le petit décalage entre ma
première condition et ma deuxième.
On appelle ceci
l'indentation, et comme
c'est écrit en gros, en gras et en rouge, c'est que c'est hyper important !
En effet, pour vous repérer dans vos futurs programmes, cela sera très
utile. Imaginez deux secondes que vous avez un programme de 700 lignes avec 150
conditions, et que tout est écrit le long du bord gauche. Vous allez vous amuser
pour retrouver où commence et où se termine une condition.
Je vous le
garantis !Vous n'êtes pas obligés de le faire, mais je vous assure
que vous y viendrez.
À titre d'information, n'essayez pas de faire des
comparaisons de String à ce stade.
Je vous expliquerai la marche à suivre lors du chapitre sur les fonctions.
Je vois que vous
apprenez vite : nous pouvons donc passer à la vitesse supérieure !
Voyons
tout de suite
les conditions
multiples.
Les conditions multiples
Derrière ce nom barbare, se trouve simplement une ou deux (ou X)
conditions en plus dans un
if, ou
un
else if. Nous allons maintenant
utiliser les opérateurs logiques que nous avons vus au début. Pfiou ! C'est
vieux !
Alors dans ce cas :
- SI c'est vieux et ça va aller => alors on continue.
- SINON SI c'est vieux et je ne me rappelle plus => on va relire le début !
- SINON, allons-y tout de suite
!
Voilà un bel exemple de conditions multiples ! Et je n'ai
pas été la chercher loin, celle-là. Elle ressemble beaucoup à la condition de
notre programme (plus haut).
Je sais par expérience qu'on comprend mieux avec
un exemple ; donc, allons-y...
Maintenant, nous allons vérifier si un
nombre donné appartient à un intervalle connu ; par exemple, savoir si un nombre
est entre 50 et 100. Nous allons essayer de résoudre ce problème avec les outils
que nous avons. En gros, ça donnerait quelque chose comme ça :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12 |
int i = 58;
if(i < 100)
{
if(i > 50)
System.out.println("Le nombre est bien dans l'intervalle");
else
System.out.println("Le nombre n'est pas dans l'intervalle");
}
else
System.out.println("Le nombre n'est pas dans l'intervalle");
|
Rien de bien compliqué :
notre objectif dans ce programme est de repérer si un nombre répond à deux
conditions, il faut :
- qu'il soit inférieur à 100
- qu'il soit supérieur à 50.
Eh bien les conditions
multiples peuvent éliminer deux lignes dans notre précédent code. Regardez
plutôt :
Code : Java
1
2
3
4
5
6 |
int i = 58;
if(i < 100 && i > 50)
System.out.println("Le nombre est bien dans l'intervalle");
else
System.out.println("Le nombre n' est pas dans l'intervalle");
|
Nous avons utilisé
l'opérateur
&& qui signifie
ET. Donc, la condition de notre
if est devenu :
si
i est
inférieur à 100
ET supérieur à 50,
alors la condition est remplie.
Avec l'opérateur &&, la clause est remplie si et
seulement si les conditions formant la clause sont toutes remplies ; si l'une
des conditions n'est pas vérifiée, la clause sera considérée comme
fausse.
Ici, nous avons deux conditions liées par l'opérateur
&& : les deux conditions
doivent être vraies pour que la clause soit remplie !
Cet opérateur
vous initie à la notion d'
intersection d'ensembles.
Ici, nous avons deux conditions qui définissent chacune un ensemble :
- i < 100 définit un ensemble des nombres
inférieurs à 100 (59 ou 45 ou 0 ou -1000000)
- i > 50 définit les nombres supérieurs à 50
(58 ou 79 ou 101 ou 1000000).
L'opérateur
&& permet de
faire intersection de ces ensembles. La condition regroupe donc les nombres qui
appartiennent à ces deux ensembles, ici les nombres de 51 à 99
inclus.
Réfléchissez bien à l'intervalle que vous voulez définir. Regardez ce
code :
Code : Java
1
2
3
4
5
6 |
int i = 58;
if(i < 100 && i > 100)
System.out.println("Le nombre est bien dans l'intervalle");
else
System.out.println("Le nombre n'est pas dans l'intervalle");
|
Ici, la condition ne sera
JAMAIS remplie car, personnellement, je ne connais aucun nombre
qui est à la fois plus petit que 100 et plus grand !
Par contre, si on remplace les inférieur /
supérieur stricts par des inférieur / supérieur ou égal, le seul nombre qui
puisse valider la clause est 100, car c'est le seul qui appartienne aux deux
ensembles.
Reprenez le code précédent, celui-ci où la condition ne
sera jamais remplie...
Maintenant, remplacez l'opérateur
&& par
|| (pour mémoire, c'est un OU).
À
l'exécution du programme et après plusieurs tests de valeur pour
i, vous pouvez vous apercevoir que tous les nombres
remplissent cette condition,
sauf 100.
Nous vérifions
ici si le nombre choisi appartient à
L'UN DES DEUX
ensembles ou aux DEUX. On cherche un nombre strictement
inférieur à 100 OU un nombre strictement supérieur à 100 : donc tous les nombres
remplissent cette condition,
SAUF 100.
Et là, si nous
remplaçons les inégalités strictes par des inégalités larges, tous les nombres
remplissent la condition, car 100 fera partie des deux ensembles.
Ici, un seul opérateur large suffit, car si 100
appartient à l'un des deux ensembles, la condition sera remplie car le nombre
doit appartenir à l'un ou l'autre, ou aux deux intervalles !
La structure switch
Cette instruction est un peu particulière... par sa
syntaxe, et son utilisation.
Le
switch est surtout utilisé lorsque nous voulons
des conditions "à la carte". Le meilleur exemple se trouve sur le site du Zér0 :
ce n'est pas tant la note, mais l'appréciation qui suit qui est définie avec un
switch.
Prenons l'exemple d'un questionnaire de 4
questions, sur 5 points chacune, qui nous donne 5 notes, et donc 5 appréciations
possibles, comme ce qui suit :
- 0/20 : tu peux revoir ce chapitre, petit Zér0 !
- 5/20 : concentre-toi un peu plus... Allez, persévère !
- 10/20 : Je crois que tu as compris l'essentiel ! Viens
relire ce chapitre à l'occasion.
- 15/20 : BRAVO ! Voilà une note encourageante pour moi qui
essaie de vous apprendre des trucs !
- 20/20 : IL EST VRAIMENT... IL EST VRAIMENT.... IL EST
VRAIMENT PHHHEEEEEENOOOOOMMEEEEEENAAAALLLL!
Dans ce genre de
cas, on utilise un
switch pour
alléger un peu le code, et surtout pour éviter des
else if à répétition.
J'en conviens : nous pouvons très bien arriver au
même résultat avec des if qu'avec
un switch. Mais il faut le voir
tout de même.
Je vais vous expliquer comment se construit la
syntaxe d'un
switch ; puis nous allons le mettre en
pratique tout de suite après.
SyntaxeCode : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
switch (/*variable*/)
{
case /*argument*/:
/*action*/;
break;
case /*argument*/:
/*action*/;
break;
case /*argument*/:
/*action*/;
break;
default:/*action*/;
}
|
Cette expression s'exécute comme
suit :
- la classe évalue l'expression figurant après le switch (ici /* variable*/)
- la classe cherche ensuite s'il existe une languette (case /*valeur possible de la variable */:), dans le bloc
d'instructions, correspondant à la forme de /*variable*/
- s'il existe une languette, la requête figurant dans celle-ci sera exécutée
- sinon, on passe à la languette suivante !
- Si aucun cas n'a été trouvé, la classe va exécuter ce qui se trouve dans
l'instruction default:/*action*/;, voyez ceci comme une
sécurité.
Notez bien la présence de l'instruction break;. Celle-ci permet
de sortir du switch si une
languette a été trouvée pour le cas concerné. Pour mieux juger de l'utilité de
cette instruction, enlevez tous les break;, et compilez votre programme. Vous verrez
le résultat...
Voilà un exemple de
switch que vous pouvez essayer :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 |
int nbre = 5;
switch (nbre)
{
case 1: System.out.println("Ce nombre est tout petit");
break;
case 2: System.out.println("Ce nombre est tout petit");
break;
case 3: System.out.println("Ce nombre est un peu plus grand");
break;
case 4: System.out.println("Ce nombre est un peu plus grand");
break;
case 5: System.out.println("Ce nombre est la moyenne");
break;
case 6: System.out.println("Ce nombre est tout de même grand");
break;
case 7: System.out.println("Ce nombre est grand");
break;
default: System.out.println("Ce nombre est très grand, puisqu'il est compris entre 8 et 10");
}
|
Ici, vous devriez commencer
à voir l'intérêt de
l'indentation
==> je crois que je l'aurai assez dit...
Si vous avez essayé ce programme en enlevant l'instruction
break;, vous avez dû vous rendre compte que le
switch exécute le code contenu dans le
case 5: mais aussi dans tous ceux qui suivent
!
L'instruction break;
permet de sortir de l'opération en cours. Dans notre cas, on sort de
l'instruction
switch, mais vous verrez une autre
utilisation au chapitre suivant.
L'instruction switch ne prend que des entiers ou des caractères
en paramètre... C'était important de le dire.
Je pense que c'est
assez clair ! Vous pouvez voir le même résultat lorsque vous faites des "scores"
différents dans vos QCM.
Surtout, pensez bien à l'instruction
break;, et aussi à vos
;.
Si tout le
monde suit, voyons à quoi ressemble les conditions ternaires !
La condition ternaire
Celle-ci est un peu particulière mais très
pratique.
Avec elle, vous pourrez condenser certaines parties de code, mais
attention à ne pas en abuser sinon votre code sera indigeste.
La
particularité des conditions ternaires réside dans le fait que trois opérandes
(variable ou constante) sont misent en jeu mais aussi que ces conditions sont
employées pour affecter des données dans une variable. Voici à quoi ressemble la
structure de ce type de condition :
Code : Java
1
2 |
int x = 10, y = 20;
int max = (x < y) ? y : x ; //Maintenant max vaut 20
|
Décortiquons ce qu'il se
passe :
- Notre variablla valeur qui se trouve après le max est en train d'être affectée, mais
de l' autre côté de l'opérateur d'affectation, se trouve une condition
ternaire...
- Ce qui se trouve entre parenthèses est évalué : est-ce que x est plus petit que y, donc deux cas de figure se profilent à
l'horizon :
- Si la condition renvoie true (vrai), donc qu'elle est
vérifiée, la valeur qui se trouvent après le ? sera affectée.
- Sinon, la valeur se trouvant après le symbole : sera affectée.
- L'affectation est faite, vous pouvez utiliser votre variable max
.
Attention : La condition que vous évaluez doit retourner
soit vrai soit faux !
Pour vous faire voir l'utilité de ce genre
d'instruction, voilà à quoi pourrait ressembler un code qui fait exactement la
même chose que l'exemple que je vous ai fourni :
Code :
Java
1
2
3
4
5 |
int x = 10, y = 20, max = 0;
if(x < y)
max = y;
else
max = x;
|
Vous pouvez aussi faire des
calculs (ou autre chose) avant d'affecter les valeurs, donc ce code fonctionne :
Code : Java
1
2 |
int x = 10, y = 20;
int max = (x < y) ? y * 2 : x * 2 ; //Ici max vaut 2 * 20 soit 40
|
J'espère que vous y voyez
plus clair...
Cependant, vous devez savoir autre chose, comme je vous l'ai
dit lorsque vous avez lu le chapitre sur les opérateurs, vous pouvez utiliser le
modulo pour savoir si un nombre est pair ou impair. Avec le code suivant, vous
allez voir que la variable que nous souhaitons affecter n'a pas de lien avec la
condition présente dans l'instruction ternaire.
Pour preuve nous allons
affecter un
String
grâce à une condition sur deux
int :
Code : Java
1
2
3
4 |
int x = 10;
String type = (x % 2 == 0) ? "C' est pair" : "C' est impair" ; //Ici type vaut "C' est pair"
x = 9;
type = (x % 2 == 0) ? "C' est pair" : "C' est impair" ; //Ici type vaut "C' est impair"
|
Avant de vous laisser, vous
ne devez pas oublier que
la valeur que vous allez
affecté à votre variable DOIT ETRE DU MEME TYPE QUE VOTRE VARIABLE ! !
Vous devez aussi savoir que rien ne vous empêche de
mettre une condition ternaire dans une condition ternaire

:
Code : Java
1
2
3
4
5 |
int x = 10, y = 20;
int max = (x < y) ? (y < 10) ? y % 10 : y * 2 : x ; // max vaut 40
//Pas très facile à lire...
//Vous pouvez entourer votre deuxième instruction ternaire avec des parenthèses pour mieux voir
max = (x < y) ? ((y < 10) ? y % 10 : y * 2) : x ; // max vaut 40
|
Je vous propose maintenant
un petit QCM de derrière les fagots...
Je vous conseille d'utiliser ce que vous venez de voir
avec ce que vous avez appris lors du chapitre précédent ! Vous verrez tout de
suite qu'il est assez pénible de lancer l'application à chaque
fois.
Rappelez-vous de ce que je vous ai dit sur l'exécution séquentielle
d'un programme. Une fois arrivé à la fin, le programme s'arrête...
Si seulement on pouvait répéter des morceaux de codes... Ah ! Mais c'est
ce que nous verrons dans le chapitre suivant...
Je crois que vous êtes d'attaque pour le prochain chapitre :
les boucles !
GERONIMOOOOOO !
Les boucles
Autre chapitre important en Java : les boucles !
Oui, mais ...c'est quoi, ces fameuses boucles
?
Une boucle est une instruction qui permet de faire plusieurs fois la
même chose...
Très pratique lors de protocoles lourds en exécution. Comme lorsque nous
demandons si un utilisateur souhaite refaire quelque chose ou non. Ou encore,
lorsque nous devons trier des données dans un tableau... Eh bien, dans ce genre
de cas, on se sert d'une boucle !
En gros, en français ça donnerait
:
tant que nombre de lignes est
inférieur à 100,
alors fais ceci,
ou cela, mais n'oublie pas de faire ça...
Ne vous inquiétez pas : ce
chapitre est facile à digérer.
Bon appétit !
La boucle while
Décortiquons précisément ce qui se passe dans une
boucle.
Pour cela, nous allons voir comment elle se forme.
Une boucle
commence par une
déclaration. Ici :
while qui veut
dire, à peu de chose près,
TANT QUE.

Ensuite,
nous avons une
condition. C'est elle qui permet à la
boucle de s'arrêter. Une boucle n'est pratique que si nous pouvons la contrôler,
et donc lui faire répéter une instruction un certain nombre de fois. C'est à ça
que servent les conditions.
Puis nous avons
l'instruction. C'est ce que va répéter notre boucle ! Dans
une boucle, il peut y avoir une ou plusieurs instructions.
Remarque : il peut même y avoir des boucles, dans une
boucle...
À ce stade, la boucle va tester la condition, et en fonction de
celle-ci, va recommencer ou s'arrêter.
Un exemple concret est toujours le
bienvenu... Voici comment une boucle de type
while se fait en Java.
D'abord,
réfléchissons au "comment notre boucle va travailler". Pour cela, il faut
déterminer notre exemple.
Nous allons afficher "bonjour", <un prénom>
que vous allez taper à l'écran, puis vous demanderez si vous voulez recommencer.
Pour réussir ceci, il nous faut
une variable pour recevoir le
prénom, donc de type
String, et
une variable
pour récupérer votre réponse et là, plusieurs choix s'offrent à nous :
soit un caractère, soit une chaîne de caractères, soit un entier. Ici, nous
prendrons une variable de type
char.
Et c'est parti !
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 |
//Une variable vide
String prenom;
// On initialise celle-ci à O pour oui !
char reponse = 'O';
//Notre objet Scanner, n'oubliez pas l'import de java.util.Scanner
Scanner sc = new Scanner(System.in);
//Tant que la réponse donnée est égale à oui
while (reponse == 'O')
{
//On affiche une instruction
System.out.println("Donnez un prénom : ");
//On récupère le prénom saisi
prenom = sc.nextLine();
// On affiche notre phrase avec le prénom
System.out.println("Bonjour " +prenom+ " comment vas-tu ?");
//On demande si la personne veut faire un autre essai
System.out.println("Voulez-vous réessayer ?(O/N)");
//On récupère la réponse de l'utilisateur
reponse = sc.nextLine().charAt(0);
}
System.out.println("Au revoir...");
//Fin de la boucle
|
Avant tout, vous avez dû
cligner des yeux sur cette ligne "
reponse =
sc.nextLine().charAt(0);". Rappelez-vous comment on récupère un
char avec l'objet
Scanner. Nous devons récupérer un objet
String et ensuite prendre le
premier caractère de celui-ci ! Eh bien cette syntaxe est une contraction de ce
que je vous avais fait voir auparavant

.
Détaillons un peu ce qui ce passe.
Dans un premier temps, nous
avons
déclaré et initialisé nos variables.
Ensuite,
la boucle
teste la condition qui nous dit :
"Tant que la variable reponse contient OUI, on exécute la
boucle". Celle-ci contient bien la chaîne OUI, donc nous entrons dans la
boucle. Rappelez-vous qu'on teste l'égalité des conditions avec un double égal
==.
Puis c'est l'
exécution des instructions, dans l'ordre où elles
apparaissent dans la boucle.
Et à la fin, c'est-à-dire à l'accolade fermante
de la boucle, le compilateur retourne au début de la boucle.
Cette boucle n'est exécutée que lorsque la condition
est remplie : ici, nous avons initialisé la variable "reponse" à 'O' pour que la
boucle se fasse. Si nous ne l'avions pas fait, nous n'y serions jamais rentrés.
Normal, puisque nous testons la condition avant de rentrer dans la boucle
!
Voilà. C'est pas mal, mais il y a un petit problème, dans ce
programme...

Vous ne voyez pas ? Oh ! Je suis sûr qu'il y a des petits malins qui se sont
amusés à mettre autre chose que '
O' ou '
N' en "
reponse". Ces petits
filous-là ont dû remarquer que nous sortons de la boucle si on tape autre chose
que '
O'... Essayez de trouver comment pallier à ce
problème....
Il faudrait forcer les utilisateurs à ne tapez que 'OUI' ou
'NON'... Mais non, pas en leur mettant un couteau sous la gorge, bande de
barbares !
Avec une boucle !
Comment faire ? C'est très simple, vous allez voir ! Il suffit, comme je
l'ai dit plus haut, de forcer les utilisateurs à rentrer soit '
NON'soit '
OUI' ! Avec un
while ! Mais cela sous-entend de réinitialiser notre
variable
reponse à ' ' (caractère vide).
Nous
allons utiliser ici la méthode. Occupons-nous de la condition de notre boucle,
maintenant. Il faut répéter la phase "Voulez-vous réessayer ?" tant que la
"
reponse" donnée n'est pas '
OUI' et '
NON' : voilà, tout y
est.
On appelle ce genre de condition des
conditions multiples. Vous avez vu les opérateurs
logiques au chapitre 2, normalement... (et
==
&&) Nous en reparlerons dans le prochain chapitre...
Voici
notre programme dans son intégralité :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 |
//Une variable vide
String prenom;
// On initialise celle-ci à O pour oui !
char reponse = 'O';
//Notre objet Scanner, n'oubliez pas l' import de java.util.Scanner
Scanner sc = new Scanner(System.in);
//Tant que la réponse donnée est égale à oui
while (reponse == 'O')
{
//On affiche une instruction
System.out.println("Donnez un prénom: ");
//On récupère le prénom saisi
prenom = sc.nextLine();
// On affiche notre phrase avec le prénom
System.out.println("Bonjour " +prenom+ " comment vas-tu ?");
//réinitialisation de la variable réponse.
//Sans çà, nous n' entrions pas dans la deuxième boucle
reponse = ' ';
//tant que la réponse n'est pas O ou N, on repose la question
while(reponse != 'O' && reponse != 'N')
{
//On demande si la personne veut faire un autre essai
System.out.println("Voulez-vous réessayer ?(O/N)");
//On récupère la réponse de l'utilisateur
reponse = sc.nextLine().charAt(0);
}
}
//Fin de la boucle
System.out.println("Au revoir...");
|
Je vous le répète une dernière : PENSEZ A
L'INDENTATION !
Vous pouvez tester ce code (c'est
d'ailleurs vivement conseillé) : vous verrez que si vous ne rentrez pas la bonne
lettre, le programme vous demandera sans cesse votre réponse !
Attention à bien écrire vos conditions,
et à bien vérifier vos variables dans vos while, et dans toutes vos boucles en général. Sinon c'est
le drame ! Essayez le programme précédent sans la réinitialisation de la
variable reponse, et vous verrez le
résultat... on ne rentre jamais dans la 2° boucle, car "reponse" = 'O' (initialisé au
début du programme). Là, vous ne pourrez jamais changer sa valeur... donc le
programme ne s'arrêtera jamais ! On appelle ça une boucle
infinie.
Voilà un autre exemple de boucle infinie, encore
plus flagrante :
Code : Java
1
2
3
4
5 |
int a = 1, b = 15;
while (a < b)
{
System.out.println("coucou " +a+ " fois !!");
}
|
Si vous lancez ce programme,
vous allez voir une quantité astronomique de
coucou
1 fois !!, car, dans cette condition,
a sera
toujours
inférieur à
b.
Si nous voulions
faire en sorte que ce programme fonctionne comme il faut, nous aurions dû
rajouter une
instruction dans le
bloc
d'instructions de notre
while, pour changer la valeur de
a à chaque tour de boucle...
Comme ceci
:
Code : Java
1
2
3
4
5
6 |
int a = 1, b = 15;
while (a < b)
{
System.out.println("coucou " +a+ " fois !!");
a++;
}
|
Ce qui nous donnerait cela
:
Notre
a a bien augmenté de 1 à chaque
tour. Et si vous me dites que vous n'avez jamais vu
a++; je vous renvoie illico au second chapitre
==> sous chapitre 3 !
Qu'en dites-vous ? Pas trop mal, non ? Je dirais même bien !
Une petite astuce : lorsque vous n'avez qu'une
instruction dans votre boucle, vous pouvez enlever les accolades {
}, celles-ci deviennent superflues, tout comme les instructions
if, else if ou else.
Vous auriez pu aussi utiliser
cette syntaxe :
Code : Java
1
2
3 |
int a = 1, b = 15;
while (a++ < b)
System.out.println("coucou " +a+ " fois !!");
|
Rappelez-vous de ce dont je
vous parlais au chapitre précédent sur la priorité des opérateurs. Ici,
l'opérateur '
<' a la priorité sur l'opérateur
d'incrémentation '
++'. Pour faire court, la boucle
while teste la condition et ensuite incrémente la variable
a. Par contre, testez ce code :
Code : Java
1
2
3 |
int a = 1, b = 15;
while (++a < b)
System.out.println("coucou " +a+ " fois !!");
|
Vous devez remarquer qu'il y
a un tour de boucle en moins !
Eh bien, avec cette syntaxe, l'opérateur d'incrémentation est prioritaire
sur l'opérateur d'inégalité (ou égalité). C'est-à-dire que la boucle incrémente
la variable
a et, seulement après
avoir fait cela, elle teste la condition !
Avant d'aller voir un autre type de boucle, j'insiste sur le fait
que vous devez
bien réfléchir à vos conditions, ainsi qu'à vos
variables, avant de lancer une boucle, sous peine de ne jamais y
rentrer, ou comme on l'a vu, de faire une boucle infinie !
Bon, continuons
avec la boucle
do{...}while().
La boucle do....while
Vu que je viens de vous expliquer comment marche une
boucle
while, je ne vous
expliquerai que très brièvement la boucle
do...
while.
Euh... t'es sûr de ton coup, là?
Bien
entendu. En fait, ces deux boucles ne sont pas cousines, mais plutôt frangines
(soeurs, si vous préférez...). Dans le fonctionnement, elles sont identiques, à
deux détails près. Soeurs, mais pas jumelles, quoi...
Première différence
La boucle
do... while s'exécutera
au moins une
fois, contrairement à sa soeur. C'est-à-dire que la phase de test de la
condition se fait à la fin. Car la condition se met après le
while.
Deuxième différence
Différence de
syntaxe. Et elle se situe après la condition du
while.
Exemple :
Code : Java
1
2
3 |
do{
blablablablablablablabla
}while(a < b);
|
Vous voyez la différence ? Oui ?
Non ?
Il y a un ; après le while. C'est
tout ! Par contre, ne l'oubliez pas, sinon le programme ne compilera
pas.
Mis à part ces deux éléments, ces boucles fonctionnent
exactement de la même manière. D'ailleurs, nous allons refaire les deux
programmes de la boucle
while
ci-dessus, avec une boucle
do...
while. C'est parti !
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
//une variable vide !
String prenom = new String();
//pas besoin d'initialiser la variable car on entre au moins une fois dans la boucle !
char reponse = ' ';
//Notre objet Scanner, n'oubliez pas l'import de java.util.Scanner
Scanner sc = new Scanner(System.in);
do{
System.out.println("Donnez un prénom : ");
prenom = sc.nextLine();
System.out.println("Bonjour " +prenom+ ", comment vas-tu ?");
System.out.println("Voulez-vous réessayer ?(O/N)");
reponse = sc.nextLine().charAt(0);
}while (reponse == 'O');
System.out.println("Au revoir...");
|
Et faites-moi confiance : ça
marche ! Mais toujours le même problème de réponse... Voici donc le code complet
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 |
//une variable vide !
String prenom = new String();
//pas besoin d'initialiser la variable car on entre au moins une fois dans la boucle !
char reponse = ' ';
//Notre objet Scanner, n'oubliez pas l'import de java.util.Scanner
Scanner sc = new Scanner(System.in);
do{
System.out.println("Donnez un prénom : ");
prenom = sc.nextLine();
System.out.println("Bonjour " +prenom+ ", comment vas-tu ?");
do{
System.out.println("Voulez-vous réessayer ? (O/N)");
reponse = sc.nextLine().charAt(0);
}while(reponse != 'O' && reponse != 'N');
}while (reponse == 'O');
System.out.println("Au revoir...");
|
Vous voyez donc que ce code
ressemble beaucoup à celui utilisé avec la boucle
while, mais avec une petite subtilité. Avec celui-ci, plus
besoin de réinitialiser la variable
reponse, puisque, de toute manière, la boucle
s'exécutera au moins une fois !
Normalement, vous devriez avoir compris
du premier coup ! On va pouvoir se lancer sur la dernière boucle : la boucle
for.
La boucle for
Cette boucle est un peu particulière, puisque qu'elle
prend tous ses attributs dans sa condition, et agit en conséquence. Je
m'explique : jusqu'ici, nous avions fait des boucles avec :
- déclaration d'une variable avant la boucle
- initialisation de cette variable
- incrémentation de celle-ci dans la boucle.
Eh bien on met tout
ça dans la condition de la boucle
for, et c'est tout. Mais je sais bien qu'un long
discours ne vaut pas un exemple, alors voilà une boucle
for sous vos yeux ébahis :
Code : Java
1
2
3
4 |
for(int i = 1; i <= 10; i++)
{
System.out.println("Voici la ligne "+i);
}
|
Et ça nous donne :
Vous aurez sûrement remarqué la présence des ';' dans la condition pour la séparation des champs. Et là, ne
les oubliez surtout pas, sinon, le programme ne compilera pas.
Je vous
fais la même remarque que pour la boucle
while concernant les accolades...
Nous pouvons
aussi tourner la boucle dans le sens inverse. C'est-à-dire qu'au lieu de partir
de zéro pour aller à 10, nous allons aller de 10 pour atteindre 0. Comme ceci
:
Code : Java
1
2 |
for(int i = 10; i >= 0; i--)
System.out.println("Il reste "+i+" ligne(s) à écrire");
|
Ce qui nous donne :
Bien entendu, ces structures servent
essentiellement à répéter des instructions rébarbatives ; mais elles servent
aussi à faire des recherches dans certains cas de figure, ce que nous aborderons
dans un prochain chapitre.
Bon : vu que nous sommes de bons ZérOs
et que nous n'aimons pas les fautes d'orthographe, nous voulons mettre
"
ligne" au pluriel lorsqu'il nous en reste plusieurs à écrire,
et au singulier lorsqu'il nous en reste 1 ou moins ! Il va de soit que nous
allons utiliser les conditions pour réussir ce tour de force.
Je vous
laisse réfléchir 
.
Secret (cliquez
pour afficher)
Code : Java
1
2
3
4
5
6
7 |
for(int i = 10; i >= 0; i--)
{
if(i > 1)
System.out.println("Il reste "+i+" lignes à écrire");
else
System.out.println("Il reste "+i+" ligne à écrire");
}
|
Et le résultat de ce code :
Un
petit détail, tout de même... Ici, nous avons utilisé un entier bien défini pour
gérer nos boucles, tel que 0 ou 10. Nous pouvons tout aussi bien faire les mêmes
boucles avec une variable en guise d'attribut. Là, vous commencez à apercevoir
leur intérêt. Ne vous inquiétez pas : vous allez voir tout ceci plus tard. Et
plus précisément dans le chapitre sur
les tableaux, qui arrive
à pas de loup !
Et voilà : la boucle est bouclée !!
Normalement, vous n'aurez pas besoin de digestif pour ce chapitre ! Mais
on ne sait jamais...
Je crois qu'il est temps pour un petit TP !
TP n°1 : un tableau de conversion Celsius - Fahrenheit !
Voilà un très bon petit TP qui va mettre en oeuvre tout ce
que vous avez vu jusqu'ici. Mais vraiment
tout
!Accrochez-vous, car là je vais vous demander de penser à
des tonnes de choses, et vous serez tout seuls. Lâchés dans la nature... non, je
plaisante. Oui, je sais, je déconne beaucoup, tout de même !
Mais je pense
que nous apprendrons mieux dans la bonne humeur.
Bon : trêve de
bavardage, au boulot.
Élaboration
Euh... Avant de foncer têtes baissées sur Eclipse, et
commencer à coder, nous allons d'abord essayer de structurer notre futur
programme. En plus, je ne vous ai même pas dit ce que j'attendais de vous...
Cahier des charges
Alors je veux :
- un code qui puisse se répéter autant de fois que nous le
souhaitons
- que le code demande à quelle température nous commençons
la conversion
- la température de fin de conversion
- le pas de conversion => c'est-à-dire par degré, ou
tous les 2 degrés, ou les x degrés
- qu'on vérifie les cas impossibles : comme un pas plus grand que l'intervalle de température, ou
une température d'arrivée plus petite que la température
de départ
- qu'on demande à l'utilisateur s'il est prêt, ou si son
imprimante est prête... enfin ce que vous voulez, ici
- s'il est prêt, que l'on affiche les conversions sous
forme de tableau visuel
- qu'il n'y ait pas de décalage pour les différentes valeurs =>
tout doit être parfaitement aligné
- enfin, que l'on demande à l'utilisateur s'il veut faire une nouvelle
conversion, donc revenir au début, s'il le souhaite !
Je vous avais prévenus que je serais exigeant ! Mais croyez-moi,
vous êtes capables de le faire.
Je sais que vous y arriverez !
Élaboration
Comme je vous l'ai dit,
essayez de réfléchir sur papier avant... Ce qu'il vous faut comme nombre de
variables, les types de variables, comment va se dérouler le programme, les
conditions et les boucles utilisées...
Pour info, voici la formule de
conversion pour passer des degrés Celsius en degrés Fahrenheit :
F = 9/5 * C + 32Je vais vous
aiguiller un peu :
- pour ce genre de calcul, utilisez des variables de type double
- faites attention à la priorité des opérations
- de simples if... else
suffisent ici : pas besoin d'un switch.
Voici un aperçu de ce
que je vous demande :
Vous
voyez bien que tous mes chiffres sont alignés, malgré leur taille. Lors de
l'affichage, il faudra donc utiliser une condition en fonction de la taille des
chiffres (
if Celsius < 100){.........}
else{.........}).
Je vais également vous donner une fonction toute
faite, qui vous permettra d'arrondir vos résultats. Je vous expliquerai le
fonctionnement des fonctions exactement 2 chapitres plus loin. Mais en
attendant, c'est facultatif. Vous pouvez très bien ne pas vous en servir. Pour
ceux qui souhaitent tout de même l'utiliser, la voici :
Code
: Java
1
2
3 |
public static double arrondi(double A, int B) {
return (double) ( (int) (A * Math.pow(10, B) + .5)) / Math.pow(10, B);
}
|
Elle est à placer entre les deux
accolades fermantes de votre classe, comme ceci :
Vous
pouvez l'utiliser de cette manière : imaginez que vous avez la variable
Faren à arrondir, et que le
résultat obtenu soit enregistré dans une variable
arrond_Faren, vous procéderez comme suit
:
Code : Java
1
2 |
arrondFaren = arrondi(faren,1); // pour un chiffre après la virgule
arrondFaren = arrondi(faren, 2);// pour 2 chiffres après la virgule... etc.
|
Bon : Je vous ai déjà assez
aidés ! Place à la conception.
Conception
Dernière recommandation
Essayez de
bien INDENTER votre code ! Prenez votre temps. Essayez de
penser à tous les cas de figures...
Maintenant à vos papiers, crayons,
neurones et claviers, ...et bon courage !
Correction
STOP !!! C'est fini ! Passons
maintenant à la correction de ce premier TP.
Ça va ? Pas trop mal à la tête ?
Je me doute qu'il a dû y avoir quelques tubes d'aspirine d'utilisés...
Mais
vous allez voir qu'en définitive, ce TP n'était pas si compliqué.
Surtout,
n'allez pas croire que ma correction est parole d'évangile... Il y avait
différentes manières d'obtenir le même résultat. Voici tout de même une des
corrections possibles.
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 |
class Sdz1 {
public static void main(String[] args) {
//Notre objet Scanner
Scanner sc = new Scanner(System.in);
//initialisation des variables
double c, f=0;
int i,j=0;
char reponse=' ';
System.out.println(" ------------------------------------------------------------------------");
System.out.println("| CONVERSION DEGRES CELSIUS ET DEGRES FARENHEIT |");
System.out.println(" ------------------------------------------------------------------------");
do{//tant que reponse = O//boucle principale
do{//tant que l'imprimante n'est pas prête//boucle de test pour savoir si l'utilisateur est prêt
do {// tant que valeur impossible rentrée
//saisie des valeurs
System.out.println("A partir de :");//affichage des directives et récupération des données
c = sc.nextDouble();
System.out.println("jusqu' à:");
i = sc.nextInt();
System.out.println("Par pas de :");
j = sc.nextInt();
if (c > i || j > i || j == 0)
System.out.println("Traitement impossible");
}while(c > i || j > i || j == 0);
do {//tant que la reponse n'est pas O ou N
System.out.println("Assurez-vous que l'imprimante est prête");
System.out.println("Si vous êtes prêt, tapez O, sinon tapez N");
//sc.reset();
reponse = sc.next().charAt(0);
}while (reponse != 'O' && reponse != 'N');
}while (reponse == 'N');
// Traitement des valeurs
System.out.println("TABLE DE CONVERSION CELSIUS / FARENHEIT");
System.out.println("---------------------------------------------");
System.out.println(" Celsius | Farenheit ");
System.out.println("---------------------------------------------");
do{//tant que l'affichage n'est pas fini, on boucle les données et les calculs
f = ((9.0/5.0) * c) + 32.0;
if (c < 10)//si le Celsius n'a qu'un chiffre, on affiche un certain nombre d'espaces
System.out.println(" "+c+" | "+arrondi(f,1));
else
{
if(c < 100)//S'il y a un chiffre en plus, on enlève un espace blanc...
System.out.println(" "+c+" | "+arrondi(f,1));
else
System.out.println(" "+c+" | "+arrondi(f,1));
}
c = c + j;//On incrémente le degré Celsius avec le pas
}while (c <= i);
do{
System.out.println("Souhaitez-vous éditer une autre table ?(O/N)");
reponse = sc.next().charAt(0);
}while(reponse != 'O' && reponse != 'N');
}while(reponse == 'O');
System.out.println("Au revoir !");
//Fin de programme
}
public static double arrondi(double A, int B) {
return (double) ( (int) (A * Math.pow(10, B) + .5)) / Math.pow(10, B);
}
}
|
Expliquons un peu ce code
- Tout programme commence par une phase de déclaration de
variable.
- Nous affichons le titre de notre programme.
- Ensuite, vous voyez 3 do{ consécutifs,
correspondant chacun à une condition à vérifier : le choix de
l'utilisateur pour faire une nouvelle conversion, vérification si
l'utilisateur est prêt, vérification si les nombres sont cohérents.
- Nous affichons les renseignements à l'écran, et nous
récupérons les saisies clavier dans une variable.
- Si les chiffres sont incohérents, on affiche une erreur, et on
boucle sur le début.
- Une boucle pour s'assurer que l'utilisateur rentre bien O
ou N.
- À partir d'ici, les nombres sont cohérents, et l'utilisateur est prêt.
Donc on lance la conversion.
- On affiche le squelette du tableau.
- Une boucle pour afficher les différents résultats.
- Calcul de la conversion.
- Selon la taille du chiffre Celsius, on enlève (ou non) un espace à
l'affichage.
- Cette boucle sera répétée tant que le degré de
départ est plus petit que le degré de fin que vous avez
choisi.
- FIN DU PROGRAMME !
Ce programme n'est pas parfait, loin de
là... La vocation de celui-ci était de vous faire utiliser ce que vous avez
appris et je pense qu'il remplit bien sa fonction.
J'espère que vous avez apprécié ce TP.
Je sais qu'il n'était pas
facile, mais avouez-le : il vous a bien fait utiliser tout ce que vous avez vu
jusqu'ici !
Voilà : votre premier TP est fait, et bien fait !!
Je
vous conseille de vous reposer un peu, parce que ça a dû fumer dans votre boite
crânienne...
Je viens de vous faire afficher un tableau (rudimentaire à l'affichage), mais maintenant nous allons
travailler avec des tableaux en Java
! C'est parti...
Les tableaux
Comme dans tout langage de programmation qui se respecte,
Java travaille aussi avec des tableaux.
Vous verrez que ceux-ci s'avèrent
bien pratiques...
Mais un tableau... Qu'est-ce que c'est, au juste
?
Très bonne question. Vous vous doutez bien (je suppose) que les
tableaux dont nous parlons n'ont pas grand-chose à voir avec ceux que vous
connaissez ! En programmation, un tableau n'est rien d'autre qu'
une variable un peu particulière... nous allons
pouvoir lui affecter
plusieurs
valeurs, rangées de façon séquentielle,
que nous pourrons appeler grâce à un indice, ou un
compteur, si vous préférez. Il nous suffira de donner
l'emplacement du contenu dans notre variable tableau pour la sortir, travailler
avec ou encore l'afficher.
Assez bavardé : mettons-nous joyeusement au
travail !
Déclarer et initialiser un tableau
Je viens de vous expliquer, grosso-modo, ce qu'est un
tableau en programmation. Si maintenant, je vous disais qu'il y a autant de
types de tableaux que de types de variables ? Je crois voir quelques gouttes de
sueur perler sur vos fronts...
Pas de panique ! Je dirais même que c'est très logique. Comme nous l'avons
vu lors du 3° chapitre, une variable d'un type donné ne peut contenir que des
éléments de ce type.
Exemple : une variable de
type
int ne peut pas recevoir une
chaîne de caractères.
Il en est de même pour les tableaux...
un
tableau d'entiers ne pourra pas recevoir des chaînes de caractères, et vice
versa. Voyons tout de suite comment se déclare un tableau
:
<type du tableau> <nom du
tableau> [] = { <contenu du tableau>};La
déclaration ressemble beaucoup à celle d'un argument de classe quelconque, si ce
n'est la présence des crochets
[] après le nom de notre
tableau, et les accolades
{} encadrant l'initialisation de
celui-ci.
Dans la pratique, ça nous donnerait quelque chose comme ceci
:
Pour un tableau d'entiers
Code : Java
1 |
int tableauEntier[] = {0,1,2,3,4,5,6,7,8,9};
|
Pour un tableau de double
Code : Java
1 |
double tableauDouble[] = {0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0};
|
Pour un tableau de caractères
Code : Java
1 |
char tableauCaractere[] = {'a','b','c','d','e','f','g'};
|
Pour un tableau de chaînes de
caractères
Code : Java
1 |
String tableauChaine[] = {"chaine1", "chaine2", "chaine3" , "chaine4"};
|
Vous remarquez bien que la
déclaration et l'initialisation d'un tableau se font comme pour une variable
normale (
si on peut dire qu'une variable est
normale...).
Nous utilisons des ' ' pour initialiser un tableau de
caractères, des " " pour initialiser un tableau de
String, etc.
Vous pouvez aussi déclarer un tableau vide
!
Attention, votre tableau sera vide mais, il doit avoir un nombre de cases défini
!
Par exemple, si vous voulez un tableau vide de
six entiers : Code : Java
1
2
3 |
int tableauEntier[] = new int[6];
//ou encore
int[] tableauEntier2 = new int[6];
|
Cette opération est très
simple, car vraiment ressemblante à ce que vous faisiez avec vos variables ; je
vous propose donc tout de suite de voir une belle variante de ceci :
les
tableaux multi-dimensionnels.
Les tableaux multi-dimensionnels
Ici, les choses se compliquent un peu. Car un tableau
multi-dimensionnel n'est rien d'autre qu'un tableau ayant comme contenu au
minimum 2 tableaux... Je me doute bien que cette notion doit en effrayer plus
d'un, mais en réalité, elle n'est pas si difficile que ça. Comme tout ce que je
vous apprends en général !
Ben oui... Si j'y arrive, vous aussi vous pouvez y arriver ! Alors, on se
lance ? GO !
Je ne vais pas vous faire de grand laïus sur ce type de
tableau, puisque je pense sincèrement qu'un exemple vous fera beaucoup mieux
comprendre le concept. Imaginez un tableau avec deux lignes : la première
contiendra les premiers nombres pairs, et le deuxième contiendra les premiers
nombres impairs. Ce tableau s'appellera
premiersNombres. Voilà ce que cela donnerait :
Code : Java
1 |
int premiersNombres[][] = { {0,2,4,6,8},{1,3,5,7,9} };
|
Nous voyons bien ici les deux
lignes de notre tableau symbolisées par les doubles
crochets
[][]. Et comme je l'ai dit plus haut, ce genre de
tableau n'est rien d'autre que plusieurs tableaux en un. Ainsi, pour passer
d'une ligne à l'autre, nous jouerons avec la valeur du premier crochet.
Exemple :
premiersNombres[0][0] correspondra au premier
élément de la ligne paire.
Et
premiersNombres[1][0] correspondra au premier
élément de la ligne impaire.
Allez !
Un petit schéma
en guise de synthèse :
Surtout, n'oubliez pas de séparer vos différents
tableaux par une ',' et de bien mettre
le tout entre accolades. Sinon, c'est le plantage assuré, et de toutes façons,
Eclipse n'en voudra pas.
Je pense que vous savez tout ce qu'il y a
à savoir sur les tableaux. Maintenant, je vous propose de faire un peu mumuse
avec...
Utiliser et rechercher dans un tableau !
Un tableau
simpleAvant d'attaquer, je dois vous dire un truc
primordial (vous avez remarqué, c'est
écrit en gros et en rouge).
Un tableau, comme ceux
que nous avons fait ci-dessus, débute toujours à l'indice 0 !
Je m'explique : prenons l'exemple du tableau de caractères. Si vous voulez
afficher la lettre '
a' à l'écran, vous devrez taper
cette ligne de code :
Code : Java
1 |
System.out.println(tableauCaractere[0]);
|
Ce qui signifie tout bêtement
qu'un tableau, ayant 4 éléments dans son contenu, aura comme entrées possibles
0, 1, 2 ou 3. Le 0 correspond au premier élément, le 1
correspond au 2° élément, le 2 correspond au 3° élément, et le 3 correspond au
4° élément.
Une très grande partie des erreurs sur les tableaux
sont souvent dues à un mauvais indice dans celui-ci. Donc : prenez
garde...
Ce que je vous propose, c'est tout bonnement d'afficher un
des tableaux ci-dessus dans son intégralité. Et le premier qui me met ce code-là
:
Code : Java
1
2
3
4
5 |
char tableauCaractere[] = {'a','b','c','d','e','f','g'};
System.out.println(tableauCaractere[0]);
System.out.println(tableauCaractere[1]);
System.out.println(tableauCaractere[2]);
System.out.println(tableauCaractere[3]);
|
... je
l'
ASSASSINE ! Peut-être pas, quand même...

Il y a une manière beaucoup plus
classe, ou
distinguée, mais surtout beaucoup plus pratique d'afficher le contenu d'un
tableau.
Voici un parcours de tableau avec une boucle while
Code : Java
1
2
3
4
5
6
7
8 |
char tableauCaractere[] = {'a','b','c','d','e','f','g'};
int i = 0;
while (i < 4)
{
System.out.println("A l'emplacement " + i +" du tableau nous avons = " +tableauCaractere[i]);
i++;
}
|
Même résultat que précédemment, mais avec une boucle
for (à utiliser de
préférence)
Code : Java
1
2
3
4
5
6 |
char tableauCaractere[] = {'a','b','c','d','e','f','g'};
for(int i = 0; i < 4; i++)
{
System.out.println("A l'emplacement " + i +" du tableau nous avons = " +tableauCaractere[i]);
}
|
Euh... Comment fait-on si on ne connaît pas la taille
de notre tableau à l'avance ?
Décidément, vous lisez dans mes
pensées... J'allais y venir !
En fait, il existe une instruction qui
retourne la taille d'un tableau.

Il
s'agit de l'instruction
<mon
tableau>.length. Notre boucle
for pourrait donc ressembler à ceci :
Code : Java
1
2
3
4
5
6 |
char tableauCaractere[] = {'a','b','c','d','e','f','g'};
for(int i = 0; i < tableauCaractere.length; i++)
{
System.out.println("A l'emplacement " + i +" du tableau nous avons = " +tableauCaractere[i]);
}
|
Alors ? Ce n'est pas mieux
comme ça ? D'accord, je reformule ma phrase pour ceux qui ont le sens de la
contradiction.
C'est mieux comme ça ! Je vais vous donner une
preuve que vous ne pourrez pas nier.
Essayez de faire une recherche dans un des tableaux ci-dessus (pas celui
contenant des
String, nous verrons ce cas dans le
prochain chapitre). En gros : faites une saisie clavier, et regardez si celle-ci
existe dans votre tableau... Dur, dur, sans boucle... COMMENT ÇA, C'EST DUR,
MÊME AVEC UNE BOUCLE ?
Dans ce cas, je vais vous aider. Gardez la même
structure de code permettant de faire plusieurs fois la même action, et ensuite
faites une boucle de recherche incluant la saisie clavier, un message si la
saisie est trouvée dans le tableau, et un autre message si celle-ci n'est pas
trouvée. Ce qui nous donne :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 |
char tableauCaractere[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g'};
int i = 0, emplacement = 0;
char reponse = ' ',carac = ' ';
Scanner sc = new Scanner(System.in);
do {//boucle principale
do {//on répète cette boucle tant que l'utilisateur n'a pas rentré une lettre figurant dans le tableau
i = 0;
System.out.println("Rentrez une lettre en minuscule, SVP ");
carac = sc.nextLine().charAt(0);
while(i < tableauCaractere.length && carac != tableauCaractere[i])//boucle de recherche dans le tableau
i++;
if (i < tableauCaractere.length)//Si i < 7 c'est que la boucle n'a pas dépassé le nombre de cas du tableau ==> il a trouvé
System.out.println(" La lettre " +carac+ " se trouve bien dans le tableau !");
else//sinon
System.out.println(" La lettre " +carac+ " ne se trouve pas dans le tableau !");
}while(i >= tableauCaractere.length);//tant que la lettre de l'utilisateur ne correspond pas à une lettre du tableau
do{
System.out.println("Voulez-vous essayer de nouveau ? (O/N)");
reponse = sc.nextLine().charAt(0);
}while(reponse != 'N' && reponse != 'O');
}while (reponse == 'O');
System.out.println("Au revoir !..");
|
Ce qui nous donne :
Explicitons un peu ce code, et plus particulièrement la
recherche
Dans notre
while, il y
a deux conditions :
==> la première correspond au compteur. Tant que
ce compteur est inférieur ou égal au nombre d'éléments du tableau, on incrémente
notre compteur pour regarder la valeur suivante. Nous passons ainsi en revue
tout ce qui se trouve dans notre tableau.
MAIS si nous n'avions
mis que cette condition, la boucle n'aurait fait que parcourir le tableau, sans
voir si le caractère saisi correspond bien à un caractère de notre tableau, d'où
la deuxième condition.
==> la deuxième correspond à la comparaison
entre le caractère saisi et la recherche dans le tableau. Grâce à ceci, si le
caractère saisi se trouve dans le tableau, la boucle prend fin, et donc
i a une valeur inférieur à
7.
À ce stade, notre recherche est terminée. Ensuite, les conditions
coulent de source ! Si nous avons trouvé une correspondance entre le caractère
saisi et notre tableau,
i aura une
valeur inférieure à 7 (je vous rappelle qu'il n'y a que 6 entrées dans notre
tableau, puisque nous avons 7 lettres dans celui-ci, et la première entrée a
comme indice
0). Dans ce cas, nous affichons un message
positif. Et dans le cas contraire, c'est l'instruction du
else qui s'exécutera.
Vous avez dû remarquer la présence d'un i = 0; dans une boucle. Ceci est PRIMORDIAL, car
sinon, lorsque vous reviendrez au début de celle-ci, i ne vaudra plus 0, mais la dernière valeur qu'il
aura eue, après les différentes incrémentations. Si vous faites une nouvelle
recherche, vous commencerez par l'indice contenu dans i ; ce que vous ne voulez pas, puisque vous
voulez regarder depuis le début du tableau, donc 0.
Pour bien vous rendre compte de cela, essayez
le programme ci-dessus sans cette instruction : vous verrez qu'il n'y a plus de
recherche possible, ou même un gros plantage d'Eclipse...
En travaillant
avec les tableaux, vous serez confrontés, un jour ou l'autre, au message suivant
:
java.lang.ArrayIndexOutOfBoundsException.Ceci
signifie qu'une exception à été levée car vous avez essayé de lire (ou d'écrire
dans) une case qui n'a pas été définie dans votre tableau ! Nous verrons les
exceptions lorsque nous aborderons la programmation orienté objet.
Exemple
Code :
Java
1
2
3
4
5
6
7 |
String[] str = new String[10];
//L'instruction suivante va déclencher une exception
//car vous essayez d'écrire à la case 11 de votre tableau alors que celui-ci n'en contient que 10
str[11] = "Une exception";
//De même, le code ci-dessous déclenchera la même exception car vous essayez de lire
//une case non définie !
String string = str[24];
|
Faites donc bien
attention à cela, car il s'agit de l'une des erreurs commises les plus
fréquentes.
Un tableau multi-dimensionnel
Nous
allons travailler sur le tableau bi-dimensionnel vu plus haut.
Le principe
est vraiment le même que pour un tableau simple. Mais ici, il n'y a que deux
compteurs. Voici un code possible pour afficher les données par ligne,
c'est-à-dire l'intégralité du
sous-tableau nombres
pairs, puis le
sous-tableau nombres impairs
:
Avec une boucle while
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13 |
int premiersNombres[][] = { {0,2,4,6,8},{1,3,5,7,9} }, i = 0, j = 0;
while (i < 2)
{
j = 0;
while(j < 5)
{
System.out.print(premiersNombres[i][j]);
j++;
}
System.out.println("");
i++;
}
|
Et voilà le résultat
:
Je suppose que vous avez remarqué la drôle de
déclaration de variable... Vous avez le droit de faire ainsi. Mais seules les
variables ayant les crochets [] après leur nom seront
considérées comme des tableaux, les autres resteront des variables toutes
simples.
Détaillons un peu ce code
- Dans un premier temps, on initialise les variables.
- On entre ensuite dans la première boucle (qui se fera deux fois, donc
i vaut 0 la première fois, et
vaudra 1 pendant la deuxième), et on initialise j à 0.
- On entre ensuite dans la deuxième boucle, où j vaudra successivement 0, 1, 2, 3 et 4
pour afficher le contenu du tableau d'indice 0 (notre premier i).
- On sort de cette boucle ; notre i est ensuite incrémenté, et passe à 1.
- On reprend le début de la première boucle => initialisation de j à 0.
- On rentre dans la deuxième boucle, où le processus est le même que
précédemment (mais là, i vaut 1).
- Enfin, nous sortons des boucles pour finir le programme.
Ce code affiche donc le contenu des deux tableaux...
Encore heureux ! Notez bien que vous devez réinitialiser votre compteur
j avant la boucle où il est utilisé
comme argument, sinon celle-ci ne fonctionnera pas. Et cela, pour la même raison
que pour un tableau normal.
Le même résultat avec une boucle for
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12 |
int premiersNombres[][] = { {0,2,4,6,8},{1,3,5,7,9} };
for(int i = 0; i < 2; i++)
{
for(int j = 0; j < 5; j++)
{
System.out.print(premiersNombres[i][j]);
}
System.out.println("");
}
|
En bonus, voici un petit
code qui va vous afficher la suite des nombres dans l'ordre, en piochant tantôt
dans le tableau pair, tantôt dans la tableau impair :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12 |
int premiersNombres[][] = { {0,2,4,6,8},{1,3,5,7,9} };
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 2; j++)
{
System.out.print(premiersNombres[j][i]);
}
}
|
Et voilà :
Vous
avez remarqué que la différence entre ces deux codes était seulement l'ordre des
conditions dans nos boucles...
Tu ne nous avais pas parlé d'une façon de faire une
boucle for ?
Tout à
fait, d'ailleurs la voici :
Code : Java
1
2
3
4 |
String tab[] = {"toto", "titi", "tutu", "tete", "tata"};
for(String str : tab)
System.out.println(str);
|
Voici
le résultat :
Faites bien attention à ne pas confondre les deux
syntaxes ! La boucle for, ancienne
génération, prend des ; dans ses
instructions, alors que la nouvelle version prend un :.
Comme je vous l'avais dit lorsque
vous avez vu "
Les boucles", cette
syntaxe se rapproche de la boucle
foreach présente dans d'autre langages.
Cette
syntaxe signifie qu'à chaque tour de boucle, la valeur courante du tableau est
mise dans la variable
str.
Il faut IMPÉRATIVEMENT que la variable passée en premier
paramètre de la boucle for soit de
même type que la valeur de retour du tableau (une variable de type String pour un tableau de String, un int pour un tableau d'int...)
Pour vérifier que les
valeurs retournées par la boucle correspondent bien à leurs indices, vous pouvez
déclarer un entier (
i, par exemple) initialisé à 0
et incrémenté à la fin de la boucle.
Allez, c'est mon jour de bonté :
Code : Java
1
2
3
4
5
6
7
8
9 |
String tab[] = {"toto", "titi", "tutu", "tete", "tata"};
int i = 0;
for(String str : tab)
{
System.out.println("La valeur de la nouvelle boucle est : " + str);
System.out.println("La valeur du tableau à l'indice " + i +" est : " + tab[i] + "\n");
i++;
}
|
Ce qui nous donne :
Alors
? Convaincus ?
Vous pouvez voir que cette forme de boucle
for est particulièrement adpatée au parcours de
tableau !
D'accord, ça a l'air bien comme méthode, mais pour les
tableaux à deux dimensions ?
Même si vous devriez trouver la réponse
tout seuls, je vais tout de même vous la donner !

Je
vous ai dit que la variable en premier paramètre devait être du même type que la
valeur de retour du tableau. Dans le cas qui nous intéresse, que va retourner
l'instruction de la boucle
for si
on utilise un tableau à deux dimensions ? Un
tableau. Nous devrons donc mettre un tableau, du même
type que notre tableau à dimensions, en première instruction de la boucle, et
donc faire une deuxième boucle afin de parcourir le résultat obtenu
!
Voici un code qui permet d'afficher un tableau à deux dimensions de
façon conventionnelle et selon la nouvelle version du JDK 1.5 :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
String tab[][] = {{"toto", "titi", "tutu", "tete", "tata"}, {"1", "2", "3", "4"}};
int i = 0, j = 0;
for(String sousTab[] : tab)
{
i = 0;
for(String str : sousTab)
{
System.out.println("La valeur de la nouvelle boucle est : " + str);
System.out.println("La valeur du tableau à l'indice [" + j + "][" +i +"] est : " + tab[j][i] + "\n");
i++;
}
j++;
}
|
Ce qui nous donne :
Vous
pouvez donc voir que nous récupérons un tableau au parcours de la première
boucle et que nous parcourons ce même tableau afin de récupérer les valeurs de
celui-ci dans la deuxième. Simple, non ? En tout cas, je préfère nettement cette
syntaxe !
Après, c'est à vous de voir...
Vous êtes maintenant parés pour
utiliser les tableaux quels qu'ils soient !
Donc, sans plus attendre, je
vous propose un petit QCM de derrière les fagots !
Encore un chapitre bien dodu, mais je vois que vous vous
en sortez comme des pros !
Le chapitre suivant sera l'un des plus fournis en informations ; donc,
si vous sentez un peu de fatigue, reposez-vous un peu avant d'attaquer
les méthodes de classe.
Les méthodes de classe
Ce chapitre aura pour but de vous faire découvrir ce qu'on
appelle, en Java, des méthodes de classe (fonctions en C/C++), de base et très
utiles.
Java est un langage 100 % objet. Ceci signifie que tout ce qui
fait partie du langage est objet !
Nous approfondirons cette notion lorsque
nous ferons de la POO (Programmation Orienté Objet).
Pour le moment, retenez
juste que, dans chaque objet, il y a des méthodes qui font des traitements
spécifiques. Il s'agit tout bonnement d'une portion de code réutilisable
!
C'est ce que nous allons aborder ici.
Mais ce chapitre ne serait
pas drôle si nous ne nous amusions pas à en créer une ou deux pour le plaisir...
Et là, vous aurez beaucoup de choses à retenir...
Quelques méthodes bien utiles !
Voici le moment de vous présenter quelques méthodes
dont, j'en suis convaincu, vous ne pourrez plus vous passer...
toLowerCase()Cette méthode permet
de transformer toute saisie clavier de type caractère en minuscules. Elle n'a
aucun effet sur les nombres, puisqu'ils ne sont pas assujettis à cette
contrainte. Vous pouvez donc utiliser cette fonction sur une chaîne de
caractères comportant des nombres.
Elle s'utilise comme ceci :
Code : Java
1
2 |
String chaine = new String("COUCOU TOUT LE MONDE !"), chaine2 = new String();
chaine2 = chaine.toLowerCase();//donne "coucou tout le monde !"
|
toUpperCase()Celle-là est facile,
puisqu'il s'agit de l'opposée de la précédente. Elle transforme donc une chaîne
de caractères en majuscules. Et s'utilise comme suit :
Code : Java
1
2 |
String chaine = new String("coucou coucou"), chaine2 = new String();
chaine2 = chaine.toUpperCase();//donne "COUCOU COUCOU"
|
concat()Très
explicite, celle-là permet de concaténer deux chaînes de
caractères.
Code : Java
1
2 |
String str1 = new String("Coucou "), str2 = new String("toi !"), str3 = new String();
str3 = str1.concat(str2);//donne "Coucou toi !"
|
length()Celle-là permet de donner
la longueur d'une chaîne de caractères (en comptant les espaces
blancs).
Code : Java
1
2
3 |
String chaine = new String("coucou ! ");
int longueur = 0;
longueur = chaine.length();//donne 9
|
random()Cette
méthode permet de générer un nombre aléatoire, entre 0 et 1 (elle renvoie donc
un
double). Vous ne voyez pas l'utilité ? Eh bien...
Vous vous en rendrez compte lors de notre prochain TP...
Code : Java
1
2 |
double X = 0.0;
X = Math.random();//donne un nombre comme 0.0001385746329371058
|
equals()Permet de voir si deux
chaînes de caractères sont identiques. Donc, de faire des tests. C'est avec
cette fonction que vous ferez vos tests de conditions, lorsqu'il y aura des
String. Exemple concret :
Code : Java
1
2
3
4
5
6
7 |
String str1 = new String("coucou"), str2 = new String("toutou");
if (str1.equals(str2))//Si les deux chaînes sont identiques
System.out.println("Les deux chaines sont identiques !");
else
System.out.println("Les deux chaînes sont différentes !");
|
Vous pouvez aussi demander la non vérification de
l'égalité... Grâce à l'opérateur de négation... vous vous en souvenez ? Il
s'agit de '!'
Ce qui nous donne :
Code : Java
1
2
3
4
5
6
7 |
String str1 = new String("coucou"), str2 = new String("toutou");
if (!str1.equals(str2))//Si les deux chaînes sont différentes
System.out.println("Les deux chaines sont différentes !");
else
System.out.println("Les deux chaînes sont identiques !");
|
Le principe de ce genre de
condition fonctionne de la même façon pour les boucles. Et dans l'absolu, cette
fonction retourne un booléen. C'est pourquoi nous pouvons utiliser cette
fonction dans les tests de condition.
Code : Java
1
2 |
String str1 = new String("coucou"), str2 = new String("toutou");
boolean Bok = str1.equals(str2);//ici Bok prendra la valeur false
|
charAt()Le
résultat de cette méthode sera un caractère, car il s'agit d'une méthode
d'extraction de caractères, je dirais même d'
UN
caractère. Elle ne peut s'opérer que sur des
String!
Elle possède la même particularité que les tableaux, c'est-à-dire que, pour
cette méthode, le premier caractère sera le numéro
0.
Cette méthode prend un entier comme argument.
Code :
Java
1
2
3 |
String nbre = new String("1234567");
char carac = ' ';
carac = nbre.charAT(4);//renverra ici le carctère 5
|
substring()Comme son nom
l'indique, elle permet d'extraire une sous-chaîne de caractères d'une chaîne de
caractères. Cette méthode prend 2 entiers comme arguments. Le premier définit le
début de la sous-chaîne à extraire
inclus, le deuxième correspond au dernier caractère
à extraire
exclus. Et le premier
caractère est aussi le numéro
0.
Code : Java
1
2 |
String chaine = new String("la paix niche"), chaine2 = new String();
chaine2 = chaine.substring(3,13);//permet d'extraire "paix niche"
|
indexOf()/lastIndexOf()indexOf() permet d'explorer une chaîne de
caractères depuis son début.
lastIndexOf() depuis sa fin. Elle prend un
caractère, ou une chaîne de
caractères comme argument, et renvoie un
int. Tout comme
charAt() et
substring(), le premier caractère est à la place
0. Je crois qu'ici un exemple s'impose, plus encore que
pour les autres fonctions :
Code : Java
1
2
3
4
5
6
7
8 |
String mot = new String("anticonstitutionnellement");
int n = 0;
n = mot.indexOf('t'); // n vaut 2
n = mot.lastIndexOf('t'); // n vaut 24
n = mot.indexOf("ti"); // n vaut 2
n = mot.lastIndexOf("ti"); // n vaut 12
n = mot.indexOf('x'); // n vaut -1
|
Des méthodes concernant les
mathématiques
Ces méthodes utilisent la classe
Math, présente dans
java.lang. Cette classe fait donc partie des fondements du
langage et, par conséquent, aucun import particulier n'est nécessaire pour
utiliser la classe
Math.
Sinus, cosinus, tangente
Code : Java
1
2
3 |
double sin = Math.sin(120);
double cos = Math.cos(120);
double tan = Math.tan(120);
|
Ces méthodes retourne un nombre
de type
double.
Valeur absolue
Code :
Java
1 |
double abs = Math.abs(-120.25);
|
Exposant
Code :
Java
1
2
3
4
5 |
double d = 2;
double exp = Math.pow(d, 2);
//Ici on initialise la variable exp avec la valeur de d élevée au carré
//La méthode pow() prend donc une valeur en premier paramètre
//et un exposant en second
|
Je ne vais pas vous
faire un récapitulatif de toutes les méthodes présentes dans Java, sinon, dans
1000 ans, je serais encore derrière mon clavier...
Toutes ces méthodes sont très utiles, croyez-moi. Mais les plus utiles sont
encore celles que nous faisons ! C'est tellement mieux quand ça vient de nous...
Créer et utiliser sa propre méthode !
Reprenons la méthode que je vous avais donnée lors du
premier TP. Pour mémoire :
Code : Java
1
2
3 |
public static double arrondi(double A, int B) {
return (double) ( (int) (A * Math.pow(10, B) + .5)) / Math.pow(10, B);
}
|
Décortiquons un peu ceci
- Tout d' abord, il y a le mot clé public. C'est ce qui définit la
portée de la méthode. Nous y reviendrons lorsque nous programmerons des
objets.
- Ensuite il y a static. Nous y reviendrons aussi.
- Juste après, nous voyons double. Il s'agit du type de
retour de la méthode. Pour faire simple, ici, notre méthode va renvoyer un
double !
- Il y a ensuite le nom de la méthode. C'est avec ce nom
que nous l'appellerons.
- Puis, il y a les arguments que la méthode prend. C'est en
fait les paramètres dont la méthode a besoin pour travailler. Ici, nous
demandons d'arrondir le double A avec B chiffres
derrière la virgule !
- Et vous pouvez voir, à l'intérieur de la méthode, une instruction
return.
C'est cette instruction qui effectue le renvoi de la valeur, ici, un
double.
Dans ce
chapitre, nous allons voir les différents types de renvois ainsi que les
paramètres qu'une méthodes peut prendre.
Vous devez savoir deux choses
concernant les méthodes :
- elles ne sont pas limitées en nombre de paramètres
- il y a trois grands types de méthodes :
- les méthodes qui ne renvoient rien. Elles sont de type void. Ces
types de méthodes n'ont pas d'instruction return !
- les méthodes qui retournent des types primitifs (double, int...). Elles
sont de type double, int, char... Celles-ci
ont une instruction return.
- des méthodes qui retournent des objets. Par exemple, une méthode qui
retourne un objet de type String. Celles-ci aussi ont une
instruction return.
Pour le moment, nous ne faisions que des programmes
comportant une classe. Celle-ci ne comportant qu'une méthode : la méthode
main.
Le moment est donc venu de créer
vos propres méthodes. Que vous ayez utilisé la méthode
arrondi dans votre TP ou non, vous
avez du voir que celle-ci se place à l'extérieur de la méthode
main, mais dans votre classe !
Pour
rappel, voici le screen qu'il y avait dans le TP 1 :
Si vous placez une de vos méthodes à l'intérieur de la
méthode main ou à l'extérieur de votre classe : LE PROGRAMME NE
COMPILERA PAS ! !
Bon, d'accord ! C'est enregistré. Mais concrètement...
À quoi vont nous servir ces méthodes ?
Dites-vous
bien qu'un développeur est de nature assez feignante... Sans déconner. Il s'est
vite aperçu qu'il y avait du code redondant dans ses programmes... Des morceaux
de code qui faisaient toujours la même chose et qui n'avaient, comme seule
différence, la (ou les) variable(s) qu'ils traitaient.
Vu que nous venons
de voir les tableaux, nous allons donc faire des méthodes concernant ces
derniers !
Vous devez certainement vous rappeler de la façon de parcourir un
tableau...

Et
si nous faisions une méthode qui permette d'afficher le contenu d'un tableau
sans que nous soyons obligés de retaper la portion de code contenant la boucle ?
Je me doute que vous n'en voyez pas l'intérêt maintenant car, mis à part les
plus courageux d'entre vous, vous n'avez fait qu'un ou deux tableaux dans votre
main lors de la partie précédente. Et si je vous demande de déclarer 22
tableaux, si je vous dis :
"Allez, bande de ZérOs ! Parcourez-moi tout
ça !"
Vous n'allez tout de même pas faire 22 boucles
for ! De toute façon, je vous l'interdis ! Nous
allons faire une méthode. Celle-ci va :
- prendre notre tableau en paramètre (ben oui... c'est lui que nous voulons
parcourir),
- parcourir notre tableau à notre place,
- faire tous les System.out.println(),
- ne rien renvoyer.
Avec ce que nous avons défini, nous
savons donc que notre méthode sera de type
void, qu'elle prendra un tableau en paramètre
(pour le moment, on travaillera avec des tableaux de
String) et le contenu vous vous doutez de ce que
ça va être... Une boucle. Cette fameuse boucle que nous ne serons plus obligés
de répéter autant de fois que de tableaux de
String !
Ce qui va nous donner :
Code : Java
1
2
3
4
5 |
static void parcourirTableau(String[] tab)
{
for(String str : tab)
System.out.println(str);
}
|
Et voici un exemple de code
entier :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
public class Sdz1
{
public static void main(String[] args)
{
String[] tab = {"toto", "tata", "titi", "tete"};
parcourirTableau(tab);
}
static void parcourirTableau(String[] tabBis)
{
for(String str : tabBis)
System.out.println(str);
}
}
|
Je sais que ça vous trouble encore, mais sachez que
les méthodes ajoutées dans la classe main doivent être déclarées static ! Fin du mystère lors de la programmation
orientée objet !
Bon. Vous voyez que la méthode parcourt le tableau
passé en paramètre. Si vous créez plusieurs tableaux et appelez la méthode avec
ces derniers, vous vous apercevrez que la méthode affiche le contenu de chaque
tableau !
Voici une méthode au même effet que la méthode
parcourirTableau, à la différence que celle-ci
retourne une valeur. Ici, une chaîne de caractères !
Code : Java
1
2
3
4
5
6
7
8
9
10 |
static String toString(String[] tab)
{
System.out.println("Méthode toString() ! ! !\n---------------------------");
String retour = "";
for(String str : tab)
retour += str + "\n";
return retour;
}
|
Et voici un code complet : Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 |
public class Sdz1 {
public static void main(String[] args)
{
String[] tab = {"toto", "tata", "titi", "tete"};
parcourirTableau(tab);
System.out.println(toString(tab));
}
static void parcourirTableau(String[] tab)
{
for(String str : tab)
System.out.println(str);
}
static String toString(String[] tab)
{
System.out.println("Méthode toString() ! ! !\n---------------------------");
String retour = "";
for(String str : tab)
retour += str + "\n";
return retour;
}
}
|
Vous voyez que la deuxième
méthode retourne une chaîne de caractères que nous devons afficher à l'aide de
l'instruction
System.out.println().
Nous affichons la valeur renvoyée par la méthode
toString(). Alors que la méthode
parcourirTableau, elle, écrit au fur et à mesure
le contenu du tableau dans la console !
Notez que j'ai ajouté une ligne
d'écriture dans la console dans la méthode
toString() afin de vous montrer où était appelée
cette dernière.
L'instruction System.out.println() est très pratique pour
déboguer vos programmes ! Dans une méthodes, l'utilisation de cette instruction
peut faire ressortir des erreurs de conception ou de développement
!
Il nous reste un point important à aborder.
Imaginez un
instant que vous ayez plusieurs types d'éléments à parcourir ! Des tableaux à
une dimension, mais aussi d'autres à deux dimensions, et même des objets comme
des
ArrayList (nous les verrons, ne
vous inquiétez pas...). Sans aller si loin. Vous n'allez pas donner un nom
différent à la méthode
parcourirTableau à chaque type primitif
!
Vous avez dû remarquer que la méthode que nous avons créée ne prend
qu'un tableau de
String en
paramètre ! Pas de tableau d'
int ni
de
long.
Si seulement nous
pouvions utiliser la même méthode pour différents types de tableaux
!
C'est là qu'entre en jeu ce qu'on appelle : la surcharge.
La surcharge de méthode
Grâce à ceci, vous n'aurez plus à retenir 10 000 noms
de méthodes qui font la même chose !
Le principe
La surcharge de méthode
consiste à garder un nom de méthode (donc un type de traitement à faire, pour
nous, lister un tableau) et de changer la liste ou le type de ses
paramètres.
Dans le cas qui nous intéresse, nous voulons que notre
méthode
parcourirTableau puisse
parcourir n'importe quel type de tableau.
Comment faire ?
Nous allons surcharger notre
méthode afin qu'elle puisse travailler avec des
int par exemple :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11 |
static void parcourirTableau(String[] tab)
{
for(String str : tab)
System.out.println(str);
}
static void parcourirTableau(int[] tab)
{
for(int str : tab)
System.out.println(str);
}
|
Avec ces méthodes, vous
pourrez parcourir de la même manière :
- les tableaux d'entiers
- les tableaux de chaînes de caractères.
Mais vous pouvez
aussi faire de même avec les tableaux à 2 dimensions.
Voici à quoi pourrait
ressembler son code (je ne rappelle pas le code des deux méthodes ci-dessus) :
Code : Java
1
2
3
4
5
6
7
8 |
static void parcourirTableau(String[][] tab)
{
for(String tab2[] : tab)
{
for(String str : tab2)
System.out.println(str);
}
}
|
La surcharge de méthode
fonctionne aussi en ajoutant des paramètres à la méthode.
Cette méthode est
donc valide :
Code : Java
1
2
3
4
5
6
7
8 |
static void parcourirTableau(String[][] tab, int i)
{
for(String tab2[] : tab)
{
for(String str : tab2)
System.out.println(str);
}
}
|
Bon d'accord, nous ne nous
servons pas de la variable
i, mais
c'était un exemple. Comme ça, vous avez vu les cas de surcharge de méthode
!
Ce qu'il faut retenir de ce chapitre
- Les méthodes se définissent dans une classe.
- Les méthodes ne peuvent pas être
imbriquées. Elles sont déclarées les
unes après les autres.
- Les méthodes peuvent être surchargées, en changeant le type de
paramètres que celle-ci attend, le nombre de ses paramètres
ou les deux !
- Pour Java, le fait de surcharger une méthode lui indique qu'il s'agit de
2, 3 ou X méthodes différentes, car les paramètres d'appel sont
différents. Par conséquent, Java ne se trompe jamais d'appel
de méthode, puisqu'il se base sur les paramètres passés.
Je crois que vous êtes prêts pour un petit QCM des
familles !
J'espère que vous aurez appris beaucoup de choses dans
ce chapitre. En tout cas, je sais que vous en aurez besoin, et pas plus tard que
pour la partie suivante !

Prêts
pour la
programmation orientée objet
? Here we go !
J'ose espérer que vous avez apprécié ce tuto sur les bases du langage
Java ! En tout cas, je me suis bien amusé en le faisant.
Maintenant, nous
allons rentrer dans les méandres de la programmation orientée objet !
Alors
?... Toujours prêts ?
Partie 2 : Java Orienté Objet
Dans cette partie, nous allons aborder la programmation orientée objet.
Concept de programmation extrêmement puissant et pratique.
En effet, vous
verrez qu'avec ce type de programmation, vous pourrez créer, utiliser, recréer
divers objets et les utiliser dans un but que vous seuls aurez décidé.
J'aborderai ce que j'ai pu voir durant ma
formation, et uniquement cela... Mais je vous rassure : il y a déjà du
travail...
Cette partie sera extrêmement riche en concepts,
vocabulaire et méthodologie. Entre autres, vous saurez programmer en
orienté objet, vous pourrez enregistrer vos objets dans
des fichiers...
J'ajouterai aussi quelques notions de modélisation. Ceci
dans le but de vous familiariser avec la façon de schématiser des objets et
leurs interactions entre eux. Nous y reviendrons, mais il s'agira de diagrammes
de classes utilisés avec le langage
UML (Unified
Modeling Language).
Une longue introduction ne servirait à rien...
passons donc tout de suite à la première partie.
Les premiers pas en "Orienté Objet"
Dans la première partie de ce tuto sur la programmation en
Java, nous avons travaillé avec une seule classe.
Vous allez voir et
apprendre qu'en programmation orientée objet, nous travaillerons avec plusieurs
classes : en fait, autant de classes que d'objets.
Rappelez-vous de la première partie :
vous avez déjà utilisé des
objets...
Oui ! Lorsque vous faisiez ceci :
Code : Java
1 |
String str = new String("tiens... un objet String");
|
Ici
str est un objet
String. Vous avez créé un objet de la classe
String : on dit que vous avez créé
une
instance de la classe String(). Nous savons cela grâce à l'opérateur
new, qui s'utilise pour
instancier une classe.
L'objet
String, instancié
ci-dessus, a bien ses propres données : la chaîne de caractères
"tiens... un objet String". Vous devez savoir
aussi que les variables de type
String() (mais nous
préfèrerons parler d'objet à partir de maintenant) ont des méthodes associées,
comme
subString().
Je
vous sens quelque peu perplexes... mais néanmoins rassurés... Attaquons sans
plus attendre !
Les classes
Une classe peut être comparée à un moule, qui, lorsque
nous le remplissons, nous donne un objet ayant la forme du moule, et toutes ses
caractéristiques. Comme quand vous étiez enfants, quand vous vous amusiez avec
de la pâte à modeler.
Si vous avez bien suivi la première partie de ce
tuto, vous devriez savoir que notre classe contenant la méthode
main ressemble à ceci :
Code :
Java
1
2
3
4
5
6
7
8
9 |
class ClasseMain{
public static void main(String[args]){
//Vos données, variables, différents traitements....
}// fin de la méthodes main
}//fin de votre classe
|
Créez cette classe et cette
méthode
main (vous savez faire,
maintenant).
Puisque nous allons travailler en
POO, nous allons devoir créer une
seconde classe dans ce fameux projet ! Nous allons donc tout de suite créer une
classe
Ville.
Dans un but de simplicité, j'ai pensé que créer des
objets Ville vous permettrait
d'assimiler plus facilement le concept objet. Nous allons donc créer des objets
Ville avant la fin de ce
chapitre...
Allez dans
File / New / Class ou
utilisez le raccourci dans la barre d'outils, comme ceci :
Nommez
votre classe :
Ville
(convention de nommage ! !

). Mais cette fois vous ne devez pas créer la méthode
main.
Il ne peut y avoir qu'une seule méthode
main par projet ! Souvenez-vous que celle-ci est
le point de départ de votre programme ! Et un programme ne
commence qu'à
un seul endroit !
Pour être tout à fait précis, il peut exister plusieurs méthode
main dans votre projet (oui, même une par classe...)
MAIS une seule sera considérée comme le point de départ de votre
programme !Au final, vous devriez avoir ceci :
Voilà
! Maintenant vous avez votre classe, avec la méthode
main, et votre classe
Ville encore vide mais qui va bientôt pouvoir créer
des objets
Ville.
Un dernier mot cependant.
Ici, notre classe ville est précédée du mot clé "
public". Vous devez savoir que lorsque
nous créons une classe comme nous l'avons fait, Eclipse nous facilite la tache
en mettant ce mot clé. Nous verrons plus tard qu'il existe d'autres mots clé
pour définir la
portée d'une classe
(nous y viendrons). Si nous enlevons ce mot clé, l'interpréteur Java
considèrera, tacitement, votre classe comme
public.
Retenez que, par défaut et sans instructions
contraires, toutes les classes sont public !
C'est-à-dire ?
Nous verrons cela à la fin du
chapitre... Dès que nous aurons créé des objets !

Retenez
que :
Code : Java
1
2
3 |
public class UneClasse{
}//fin de votre classe
|
et
Code :
Java
1
2
3 |
class UneClasse{
}//fin de votre classe
|
Sont deux codes équivalents
!
Ce mot-clé,
public,
vous l'avez déjà rencontré lors du chapitre sur les méthodes.
Mais lorsque
public précède une méthode,
il s'agit d'un droit d'accès à une méthode membre d'une classe... c'est-à-dire
qu'avec le mot-clé
public
devant une méthode, celle-ci sera accessible par toutes les classes utilisant
cette dernière. Bien entendu, nous aurons besoin de ce genre de méthode, mais
nous aurons aussi besoin d'autres méthodes dont nous ne nous servirons que dans
la classe où celle-ci sera créée...
Dans ce cas de figure, nous
utiliserons le mot clé
private. Ce qui signifie que notre méthode ne
pourra être appelée que dans la classe où elle a vu le jour !
Il en va de même pour les variables. Nous allons
voir, dans la deuxième sous-partie, que nous pouvons protéger des variables
grâce au mot-clé private. Le principe sera le même
que pour les méthodes... Ces variables ne seront accessibles que dans la classe
où elles seront nées...
Bon !... Toutes les conditions sont réunies
pour pouvoir commencer activement la programmation orientée objet ! Et si nous
allions créer notre première ville ?
Les constructeurs
Derrière ce mot un peu barbare, se cache une notion
toute bête. Vu que notre objectif dans ce chapitre est de construire un objet
Ville, il va falloir définir les
données qu'on va lui attribuer.
Nous dirons qu'un objet
Ville a :
- un nom sous forme de chaîne de caractères,
- un nombre d'habitants sous la forme d'un entier,
- un pays apparenté sous la forme d'une chaîne de caractères.
Je suis bien d'accord ! Mais comment fait-on pour dire
à notre programme que notre objet a tout ça ?
Tout simplement en
mettant des variables (
dites d'instances) dans notre
classe.
Celle-ci va contenir une variable dont le rôle sera de stocker le
nom, une autre stockera le nombre d'habitants et la dernière se chargera du pays
!

Voilà
à quoi ressemble notre classe
Ville à présent :
Code : Java
1
2
3
4
5
6
7 |
public class Ville{
String nomVille;
String nomPays;
int nbreHabitant;
}
|
Rappelez-vous que, par
défaut, les variables d'instances présentes dans une classe sont
public.
Pourquoi tu dis variables
d'instances ?
Tout simplement parce que dans nos futures classes
Java, qui définiront des objets, il y aura plusieurs types de variables dans
celles-ci (nous approfondirons ceci dans ce chapitre).
Pour le moment, sachez
qu'il y a trois grands types de variables dans une classe objet :
- les variables d'instances : ce sont elles qui définiront
les caractéristiques de notre objet ;
- les variables de classes : celles-ci sont communes à
toutes les instances de votre classe ;
- les variables locales : ce sont des variables que nous
utiliserons pour travailler dans notre objet.
Dans l'immédiat,
nous allons travailler avec des variables d'instances afin de créer des objets
différents. Il nous reste plus qu'à créer notre premier objet mais pour ce
faire, nous allons devoir créer ce qu'on appelle des
constructeurs.
Un constructeur est une
méthode (ou méthode d'instance, vu qu'elle interagit avec une instance de votre
classe) qui va se charger de créer un objet et, le cas échéant, d'initialiser
ses variables de classe ! Cette méthode a pour rôle de dire à la
JVM de réserver de l'allocation mémoire
pour notre futur objet et donc, par extension, d'en réserver pour toutes les
variables d'instances et variables de classes de cette dernière !
Notre
premier constructeur sera ce qu'on appelle communément
un constructeur
par défaut.
C'est-à-dire qu'il ne prendra aucun paramètre mais
permettra tout de même d'instancier un objet et vu que nous sommes
perfectionnistes, nous allons initialiser nos variables d'instances.
Voici
votre premier constructeur :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 |
public class Ville{
/**
* Stocke le nom de notre ville
*/
String nomVille;
/**
* Stocke le nom du pays de notre ville
*/
String nomPays;
/**
* Stocke le nombre d'habitants de notre ville
*/
int nbreHabitant;
/**
* Constructeur par défaut
*/
public Ville(){
System.out.println("Création d'une ville !");
nomVille = "Inconnu";
nomPays = "Inconnu";
nbreHabitant = 0;
}
}
|
Vous avez remarqué que le
constructeur est en fait une méthode qui n'a aucun type de retour (
void,
double...) et qui porte
le même nom que notre classe !
Ceci est
une règle immuable : le (les) constructeur(s) d'une classe doit (doivent)
porter le même nom que la classe !
Une classe peut avoir plusieurs constructeurs
?
Bien sûr !
Il s'agit de la même méthode, mais surchargée ! Dans notre premier
constructeur nous n'avons passé aucun paramètre, mais nous allons bientôt en
mettre

.
Vous pouvez d'ores et déjà créer une instance de ville.
Mais tout
d'abord, rappelez-vous qu'une instance d'objet se fait grâce au mot-clé
new. Comme lorsque vous créez une
variable de type
String. Vous avez sûrement déjà dû
faire ce genre de déclaration :
Code : Java
1 |
String mot = new String();
|
Maintenant, vu que nous
allons créer des objets
Ville, nous
allons procéder comme pour les
String.
Vérifions que l'instanciation se fait bien. Allez dans votre classe
contenant la méthode
main et instancions un objet
Ville. Je suppose que
vous avez deviné que le type de notre objet ne sera pas
double,
int ou
long mais bien
Ville
!
Code : Java
1
2
3
4
5
6
7
8
9 |
public class Sdz1{
public static void main(String[] args){
Ville ville = new Ville();
}
}
|
Exécutez ce code, et voilà !
Je sais bien que, pour le moment, il ne sert à rien...
Mais vous devez passer par là afin de comprendre le principe de la
POO.
Maintenant, nous devons mettre des données dans
notre objet, ceci afin de pouvoir commencer à travailler...
Le but final
serait d'avoir une déclaration d'objet se faisant comme ceci :
Code : Java
1 |
Ville ville1 = new Ville("Marseille", 123456789, "France");
|
Vous avez remarqué
qu'ici les paramètres sont renseignés : eh bien il suffit de faire une méthode
qui récupère ces paramètres, et initialise les variables de notre objet. Notre
constructeur d'initialisation sera créé.
Voici le constructeur de notre objet
ville, celui qui permet d'avoir des objets avec
des paramètres différents. Comme je suis sympa, voici toute la classe telle
qu'elle est maintenant :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 |
public class Ville {
/**
* Stocke le nom de notre ville
*/
String nomVille;
/**
* Stocke le nom du pays de notre ville
*/
String nomPays;
/**
* Stocke le nombre d'habitants de notre ville
*/
int nbreHabitant;
/**
* Constructeur par défaut
*/
public Ville(){
System.out.println("Création d'une ville !");
nomVille = "Inconnu";
nomPays = "Inconnu";
nbreHabitant = 0;
}
/**
* Constructeur d'initialisation
* @param pNom
* Nom de la Ville
* @param pNbre
* Nombre d'habitants
* @param pPays
* Nom du pays
*/
public Ville(String pNom, int pNbre, String pPays)
{
System.out.println("Création d'une ville avec des paramètres !");
nomVille = pNom;
nomPays = pPays;
nbreHabitant = pNbre;
}
}
|
Dans ce cas, l'exemple de
déclaration et d'initialisation d'un objet
ville que je vous ai montré un peu plus haut
fonctionne sans aucun souci ! MAIS il vous faudra respecter scrupuleusement
l'ordre des paramètres passés lors de l'initialisation de votre objet, sinon,
c'est l'erreur de compilation à coup sûr !
Ainsi :
Code
: Java
1
2 |
Ville ville1 = new Ville("marseille", 123456789, "France");//l'ordre est respecté => aucun souci
Ville ville2 = new Ville(12456, "France", "Lille");//Erreur dans l'ordre des paramètres => erreur de compilation au final
|
Testez ce code dans votre
méthode
main :
Code :
Java
1
2 |
Ville ville = new Ville();
Ville ville2 = new Ville("Marseille", 123456789, "France");
|
Voici le résultat :
Par
contre, notre objet à un gros défaut...
Les variables d'instances qui le caractérisent sont
accessibles dans votre classe contenant votre main
!
Ceci veut dire que vous pouvez modifier les attributs d'une classe
directement. Testez ce code et vous verrez :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
public class Sdz1 {
public static void main(String[] args)
{
Ville ville = new Ville();
System.out.println(ville.nomVille);
ville.nomVille = "la tête à toto ! ! ! !";
System.out.println(ville.nomVille);
Ville ville2 = new Ville("Marseille", 123456789, "France");
ville2.nomPays = "La tête à tutu ! ! ! ! ";
System.out.println(ville2.nomPays);
}
}
|
Et le résultat :
Vous
voyez que nous pouvons accéder aux variables d'instances en utilisant le
"
.". Comme lorsque vous appelez la
méthode
subString() de l'objet
String.
C'est très risqué et la plupart
des programmeurs Java vous le diront.
Pour la plupart des cas, nous allons
contrôler les modifications des variables de classe de manière à ce qu'un code
ne fasse pas n'importe quoi avec nos objets !
C'est pour cela que nous
protégeons nos variables d'instances en les déclarant
private. Comme ceci
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 |
public class Ville {
/**
* Stocke le nom de notre ville
*/
private String nomVille;
/**
* Stocke le nom du pays de notre ville
*/
private String nomPays;
/**
* Stocke le nombre d'habitants de notre ville
*/
private int nbreHabitant;
/**
* Constructeur par défaut
*/
public Ville(){
System.out.println("Création d'une ville !");
nomVille = "Inconnu";
nomPays = "Inconnu";
nbreHabitant = 0;
}
/**
* Constructeur d'initialisation
* @param pNom
* Nom de la Ville
* @param pNbre
* Nombre d'habitants
* @param pPays
* Nom du pays
*/
public Ville(String pNom, int pNbre, String pPays)
{
System.out.println("Création d'une ville avec des paramètres !");
nomVille = pNom;
nomPays = pPays;
nbreHabitant = pNbre;
}
}
|
Et, si vous n'avez pas effacé
les lignes de code où nous avons modifié les attributs de nos objets
Ville, vous devez voir
qu'Eclipse n'apprécie pas du tout que vous tentiez d'accéder à des variables de
classe
privé !
Ces attributs ne sont plus accessibles en dehors de
la classe où ils sont déclarés !
À partir de maintenant,
ce ne sera plus le programme instanciant une classe qui ira voir ou modifier les
attributs de notre objet, mais notre objet qui renverra les informations (ou les
modifiera) lorsque le programme lui demandera !
Bon... Vous avez fait le
plus dur ! Si, si, je vous assure !
Maintenant, il va falloir se servir de ces objets... Eh oui ! Le but est
tout de même d'utiliser nos objets dans des programmes.
Pour pouvoir accéder
aux données de nos objets, nous allons utiliser ce que l'on appelle des
ACCESSEURS et pour modifier les
données, on appelle ça des
MUTATEURS. Donc que dire, si ce n'est :
"À
l'abordage, moussaillons !

"
Votre objet sait parler : accesseurs et mutateurs
Un accesseur est une méthode qui va nous permettre
d'accéder aux variables de nos objets en lecture et un mutateur, en écriture
!
Reprenons là où nous nous étions arrêtés.
Vous avez une
classe
Ville qui crée vos
objets.
Vous avez une classe avec une méthode
main, prête à accueillir vos objets, et tout ce que
vous voudrez en faire ! Mais voilà... pas moyen de faire quelque chose de ces
satanés objets ! Eh bien... le calvaire est presque terminé ! Grâce aux
accesseurs, vous pourrez afficher les variables de vos objets, et grâce aux
mutateurs, les modifier.
Voilà à quoi ressemblent les accesseurs et les
mutateurs :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 |
public class Ville {
/**
* Stocke le nom de notre ville
*/
private String nomVille;
/**
* Stocke le nom du pays de notre ville
*/
private String nomPays;
/**
* Stocke le nombre d'habitants de notre ville
*/
private int nbreHabitant;
/**
* Constructeur par défaut
*/
public Ville(){
System.out.println("Création d'une ville !");
nomVille = "Inconnu";
nomPays = "Inconnu";
nbreHabitant = 0;
}
/**
* Constructeur d'initialisation
* @param pNom
* Nom de la Ville
* @param pNbre
* Nombre d'habitants
* @param pPays
* Nom du pays
*/
public Ville(String pNom, int pNbre, String pPays)
{
System.out.println("Création d'une ville avec des paramètres !");
nomVille = pNom;
nomPays = pPays;
nbreHabitant = pNbre;
}
//*****************************************************************************************
// ACCESSEURS
//*****************************************************************************************
/**
* Retourne le nom de la ville
* @return le nom de la ville
*/
public String getNom()
{
return nomVille;
}
/**
* Retourne le nom du pays
* @return le nom du pays
*/
public String getNomPays()
{
return nomPays;
}
/**
* Retourne le nombre d'habitants
* @return nombre d'habitants
*/
public int getNombreHabitant()
{
return nbreHabitant;
}
//*****************************************************************************************
// MUTATEURS
//*****************************************************************************************
/**
* Définit le nom de la ville
* @param pNom
* nom de la ville
*/
public void setNom(String pNom)
{
nomVille = pNom;
}
/**
* Définit le nom du pays
* @param pPays
* nom du pays
*/
public void setNomPays(String pPays)
{
nomPays = pPays;
}
/**
* Définit le nombre d'habitants
* @param nbre
* nombre d'habitants
*/
public void setNombreHabitant(int nbre)
{
nbreHabitant = nbre;
}
}
|
Nos accesseurs sont bien des
méthodes, et elles sont
public pour que vous puissiez y accéder dans une
autre classe que celle-ci (la classe
main, par
exemple..

).
Les accesseurs sont du même type que la variable qu'ils doivent
retourner. Ça semble logique, non ?
Les mutateurs sont, par contre, de type
void. Ce mot clé signifie
"rien" ; en effet, ces méthodes ne
retournent aucune valeur, elles se contentent de les mettre à jour.
Je vous ai fait faire la différence entre
accesseurs et mutateurs mais, généralement, lorsqu'on parle d'accesseurs, ce
terme englobe aussi les mutateurs.
Autre chose : il s'agit ici d'une
question de convention de nommage. Les accesseurs commencent par get et les mutateurs par set, comme vous pouvez le voir ici. On parle
d'ailleurs de Getters et de
Setters.
Essayez ce code dans votre méthode
main :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 |
Ville v = new Ville();
Ville v1 = new Ville("marseille", 123456, "france");
Ville v2 = new Ville("rio", 321654, "brésil");
System.out.println("\n v = "+v.getNom()+" ville de "+v.getNombreHabitant()+ " habitants se situant en "+v.getNomPays());
System.out.println(" v1 = "+v1.getNom()+" ville de "+v1.getNombreHabitant()+ " habitants se situant en "+v1.getNomPays());
System.out.println(" v2 = "+v2.getNom()+" ville de "+v2.getNombreHabitant()+ " habitants se situant en "+v2.getNomPays()+"\n\n");
/*Nous allons interchanger les Villes v1 et v2
tout ça par l'intermédiaire d'un autre objet Ville
*/
Ville temp = new Ville();
temp = v1;
v1 = v2;
v2 = temp;
System.out.println(" v1 = "+v1.getNom()+" ville de "+v1.getNombreHabitant()+ " habitants se situant en "+v1.getNomPays());
System.out.println(" v2 = "+v2.getNom()+" ville de "+v2.getNombreHabitant()+ " habitants se situant en "+v2.getNomPays()+"\n\n");
/*nous allons maintenant interchanger leurs noms
cette fois par le biais de leur accesseurs
*/
v1.setNom("Hong Kong");
v2.setNom("Djibouti");
System.out.println(" v1 = "+v1.getNom()+" ville de "+v1.getNombreHabitant()+ " habitants se situant en "+v1.getNomPays());
System.out.println(" v2 = "+v2.getNom()+" ville de "+v2.getNombreHabitant()+ " habitants se situant en "+v2.getNomPays()+"\n\n");
|
À la compilation, vous
devriez obtenir ceci :
Vous
voyez bien que les constructeurs ont fonctionné, que les accesseurs tournent à
merveille, et que vous pouvez commencer à travailler avec vos objets
Ville.
Mais la gymnastique des doigts à effectuer pour
afficher les caractéristiques de nos objets... pfiou !...
T'aurais pu faire plus simple ! Si seulement notre objet
pouvait faire ça tout seul...
Il peut le faire !
Qu'est-ce qu'on attend ? Ben rien... on y va !
Travaillez avec votre objet : les méthodes
Bon, alors là... c'est une formalité ! Vous savez
quasiment tout.
Mais attends... tu vas nous reparler des méthodes,
alors que tu ne fais que ça depuis le début de ce chapitre ?
Eh bien
oui ! Ceci fait partie de la méthodologie que l'on m'a enseignée en formation,
même si tout ce que nous avons fait depuis le début se rapporte aux méthodes, il
faut différencier :
- les constructeurs => méthodes servant à créer des objets
- les accesseurs => méthodes servant à accéder aux données de nos objets
- et les méthodes de classe => méthodes servant à la gestion de nos
objets.
Les méthodes de classe vont vous permettre de gérer,
éditer, afficher... faire tout ce que vous voudrez avec vos objets.
Il ne
vous reste plus qu'à trouver des méthodes qui fassent quelque chose pour vos
objets...
Avec nos objets
ville, nous sommes
un peu limités en choix de méthodes... Mais nous pouvons tout de même en faire
une ou deux pour l'exemple :
- faire un système de catégories de villes par rapport à leur nombre
d'habitants ( <1000 => A, <10 000 => B...). Ceci déterminé à la
construction ou à la redéfinition du nombre d'habitants, donc ajouter une
variable d'instance de type char à notre classe, appelons-la categorie. Penser à ajouter le traitement aux bons
endroits.
- faire une méthode de description de notre objet Ville
- une méthode pour comparer deux objets par rapport à leur nombre
d'habitants.
Nous voulons que la classe Ville gère la façon de déterminer
la catégorie elle-même et non que cette action puisse être opérée de
l'extérieur. La méthode qui fera ceci sera donc déclarée private.
Par contre,
un problème va se poser ! Vous savez déjà qu'en Java on appelle des méthodes
grâce à notre variable qui nous sert de référence, celle-ci suivie de
l'opérateur "
.", puis du nom de la
dite méthode.
Par exemple :
Code : Java
1
2 |
String str = new String("opizrgpinbzegip");
str = str.subString(0,4);
|
Comment fait-on référence aux données de notre objet
dans notre classe ?
En fait, c'est tout simple ! Encore un mot clé à
retenir... Cette fois, il s'agit du mot-clé
this.
Voici tout d'abord le code de notre
class Ville en entier, c'est-à-dire avec nos méthodes
associées :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199 |
public class Ville {
/**
* Stocke le nom de notre ville
*/
private String nomVille;
/**
* Stocke le nom du pays de notre ville
*/
private String nomPays;
/**
* Stocke le nombre d'habitants de notre ville
*/
private int nbreHabitant;
/**
* Stocke le type de notre ville
*/
private char categorie;
/**
* Constructeur par défaut
*/
public Ville(){
nomVille = "Inconnu";
nomPays = "Inconnu";
nbreHabitant = 0;
this.setCategorie();
}
/**
* Constructeur d'initialisation
* @param pNom
* Nom de la Ville
* @param pNbre
* Nombre d'habitants
* @param pPays
* Nom du pays
*/
public Ville(String pNom, int pNbre, String pPays)
{
nomVille = pNom;
nomPays = pPays;
nbreHabitant = pNbre;
this.setCategorie();
}
//*****************************************************************************************
// ACCESSEURS
//*****************************************************************************************
/**
* Retourne le nom de la ville
* @return le nom de la ville
*/
public String getNom()
{
return nomVille;
}
/**
* Retourne le nom du pays
* @return le nom du pays
*/
public String getNomPays()
{
return nomPays;
}
/**
* Retourne le nombre d'habitants
* @return nombre d'habitants
*/
public int getNombreHabitant()
{
return nbreHabitant;
}
/**
* Retourne la catégorie de la ville
* @return catégorie de la ville
*/
public char getCategorie()
{
return categorie;
}
//*****************************************************************************************
// MUTATEURS
//*****************************************************************************************
/**
* Définit le nom de la ville
* @param pNom
* nom de la ville
*/
public void setNom(String pNom)
{
nomVille = pNom;
}
/**
* Définit le nom du pays
* @param pPays
* nom du pays
*/
public void setNomPays(String pPays)
{
nomPays = pPays;
}
/**
* Définit le nombre d'habitants
* @param nbre
* nombre d'habitants
*/
public void setNombreHabitant(int nbre)
{
nbreHabitant = nbre;
this.setCategorie();
}
//*****************************************************************************************
// METHODES DE CLASSE
//*****************************************************************************************
/**
* Définit la catégorie de la ville
*/
private void setCategorie() {
if (this.nbreHabitant < 10000000) {
if (this.nbreHabitant < 5000000) {
if (this.nbreHabitant < 1000000) {
if (this.nbreHabitant < 500000) {
if (this.nbreHabitant < 100000) {
if (this.nbreHabitant < 10000) {
if (this.nbreHabitant < 1000) {
if (this.nbreHabitant > 0)
this.categorie = 'A';
else
this.categorie = '?';
} else
this.categorie = 'B';
} else
this.categorie = 'C';
} else
this.categorie = 'D';
} else
this.categorie = 'E';
} else
this.categorie = 'F';
} else
this.categorie = 'G';
} else
this.categorie = 'H';
}
/**
* Retourne la description de la ville
* @return description ville
*/
public String decrisToi(){
return "\t"+this.nomVille+" est une ville de "+this.nomPays+", elle comporte : "+this.nbreHabitant+
" => elle est donc de catégorie : "+this.categorie;
}
/**
* Retourne une chaîne de caractères selon le résultat de la comparaison
* @param v1
* objet Ville
* @return comparaison de deux ville
*/
public String comparer(Ville v1){
String str = new String();
if (v1.getNombreHabitant() > this.nbreHabitant)
str = v1.getNom()+" est une ville plus peuplée que "+this.nomVille;
else
str = this.nomVille+" est une ville plus peuplée que "+v1.getNom();
return str;
}
}
|
Pour simplifier, this fait référence à l'objet courant !
Pour expliciter le fonctionnement du mot clé
this, prenons l'exemple de la
méthode
comparer(Ville V1).
La
méthode va s'appeler comme suit :
Code : Java
1
2
3
4 |
Ville V = new Ville("lyon", 654, "france");
Ville V2 = new Ville("lille", 123, "france");
V.comparer(V2);
|
Dans cette méthode, nous
voulons comparer les nombres d'habitants des deux objets
ville. Pour accéder à la variable
nbreHabitant de l'objet
V2, il
suffit d'utiliser la syntaxe
V2.getNombreHabitant() ; nous ferons donc
référence à la propriété
nbreHabitant de l'objet
V2.
Mais l'objet
V, lui, est l'objet appelant de cette méthode. Pour se
servir de ses variables, on utilise alors
this.nbreHabitant, ce qui a pour effet de faire
appel à la variable
nbreHabitant de
l'objet appelant la méthode
comparer(Ville
V).
Explicitons un peu ces trois méthodes.
La méthode
categorie();Elle ne prend aucun
paramètre, et ne renvoie rien : elle se contente de mettre à jour la variable de
classe
categorie. Elle regarde, par
rapport au nombre d'habitants de l'objet appelant, utilisé grâce au mot clé
this, dans quelle tranche se
trouve la ville en question. Selon le nombre d'habitants, le caractère renvoyé
changera. Nous l'appelons lorsque nous construisons un objet
Ville avec ou sans paramètre,
mais aussi lorsque nous redéfinissons le nombre d'habitants : de cette manière,
la catégorie est mise à jour automatiquement, sans faire appel à la
méthode.
Vous constaterez aussi que nous n'avons pas créé de mutateur pour la
variable d'instance
categorie :
nous avons en effet décidé que c'est à l'objet de gérer cette variable !
La méthode
decrisToi()Celle-ci nous renvoie un objet de
type
String. Elle fait référence aux variables qui
composent l'objet appelant la méthode, toujours grâce à
this, et nous renvoie donc une chaîne de caractères
qui nous décrit l'objet, en énumérant ses composants.
La méthode
comparer(Ville
V1)Elle prend une ville en paramètre, pour pouvoir comparer
les variables
nbreHabitant de
l'objet appelant la méthode, et de celui passé en paramètre pour nous dire
laquelle est la plus peuplée !
Bien entendu, vous pouvez créer vos propres
méthodes, avec leurs paramètres, leurs types, etc. Je ne vous oblige en rien à
faire exactement les mêmes que moi...
Les choses doivent vous
sembler plus claires...

Si
nous faisions un petit test... Essayez le code suivant dans votre méthode
main :
Code : Java
1
2
3
4
5
6
7
8 |
Ville v = new Ville();
Ville v1 = new Ville("marseille", 1236, "france");
Ville v2 = new Ville("rio", 321654, "brésil");
System.out.println("\n\n"+v1.decrisToi());
System.out.println(v.decrisToi());
System.out.println(v2.decrisToi()+"\n\n");
System.out.println(v1.comparer(v2));
|
Ce qui devrait vous donner
:
Je
viens d'avoir une idée ! Et si nous essayons de savoir combien de villes nous
avons créé ?
Comment faire ?
Avec une variable de classe !
Les variables de classes
Comme je vous le disais au début de ce chapitre, il y a
plusieurs type de variables dans une classe.
Nous avons vu les variables
d'instances qui forment la carte d'identité d'un objet et maintenant, voici les
variables de classes.
Celles-ci peuvent être très pratiques. Dans notre
exemple, nous allons pouvoir compter le nombre d'instances de notre classe
Ville,
mais vous pouvez en utiliser pour d'autres choses (un taux de TVA dans une
classe qui calcule le prix TTC, par exemple).
La particularité de ce type
de variable, c'est qu'elles seront communes à toutes les instances de la classes
!
Créons sans plus attendre notre compteur d'instance. Il s'agira d'une
variable de type
int que nous appellerons
nbreInstance ; celle-ci sera
public et nous ferons
aussi son homologue en
private : appellons-la
nbreInstanceBis (un accesseur sera nécessaire
pour accéder à cette dernière).
Pour qu'une variable soit une variable de
classe, elle doit être précédée du mot clé
static. Ce qui nous donnerait dans notre
classe
Ville :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221 |
public class Ville {
/**
* Variables publiques qui comptent les instances
*/
public static int nbreInstance = 0;
/**
* Variable privée qui comptera aussi les instances
*/
private static int nbreInstanceBis = 0;
/**
* Stocke le nom de notre ville
*/
private String nomVille;
/**
* Stocke le nom du pays de notre ville
*/
private String nomPays;
/**
* Stocke le nombre d'habitants de notre ville
*/
private int nbreHabitant;
/**
* Stocke le type de notre ville
*/
private char categorie;
/**
* Constructeur par défaut
*/
public Ville(){
//On incrémente nos variables à chaque appel aux constructeurs
nbreInstance++;
nbreInstanceBis++;
nomVille = "Inconnu";
nomPays = "Inconnu";
nbreHabitant = 0;
this.setCategorie();
}
/**
* Constructeur d'initialisation
* @param pNom
* Nom de la Ville
* @param pNbre
* Nombre d'habitants
* @param pPays
* Nom du pays
*/
public Ville(String pNom, int pNbre, String pPays)
{
//On incrémente nos variables à chaque appel aux constructeurs
nbreInstance++;
nbreInstanceBis++;
nomVille = pNom;
nomPays = pPays;
nbreHabitant = pNbre;
this.setCategorie();
}
//*****************************************************************************************
// ACCESSEURS
//*****************************************************************************************
public static int getNombreInstanceBis()
{
return nbreInstanceBis;
}
/**
* Retourne le nom de la ville
* @return le nom de la ville
*/
public String getNom()
{
return nomVille;
}
/**
* Retourne le nom du pays
* @return le nom du pays
*/
public String getNomPays()
{
return nomPays;
}
/**
* Retourne le nombre d'habitants
* @return nombre d'habitants
*/
public int getNombreHabitant()
{
return nbreHabitant;
}
/**
* Retourne la catégorie de la ville
* @return catégorie de la ville
*/
public char getCategorie()
{
return categorie;
}
//*****************************************************************************************
// MUTATEURS
//*****************************************************************************************
/**
* Définit le nom de la ville
* @param pNom
* nom de la ville
*/
public void setNom(String pNom)
{
nomVille = pNom;
}
/**
* Définit le nom du pays
* @param pPays
* nom du pays
*/
public void setNomPays(String pPays)
{
nomPays = pPays;
}
/**
* Définit le nombre d'habitants
* @param nbre
* nombre d'habitants
*/
public void setNombreHabitant(int nbre)
{
nbreHabitant = nbre;
this.setCategorie();
}
//*****************************************************************************************
// METHODES DE CLASSE
//*****************************************************************************************
/**
* Définit la catégorie de la ville
*/
private void setCategorie() {
if (this.nbreHabitant < 10000000) {
if (this.nbreHabitant < 5000000) {
if (this.nbreHabitant < 1000000) {
if (this.nbreHabitant < 500000) {
if (this.nbreHabitant < 100000) {
if (this.nbreHabitant < 10000) {
if (this.nbreHabitant < 1000) {
if (this.nbreHabitant > 0)
this.categorie = 'A';
else
this.categorie = '?';
} else
this.categorie = 'B';
} else
this.categorie = 'C';
} else
this.categorie = 'D';
} else
this.categorie = 'E';
} else
this.categorie = 'F';
} else
this.categorie = 'G';
} else
this.categorie = 'H';
}
/**
* Retourne la description de la ville
* @return description ville
*/
public String decrisToi(){
return "\t"+this.nomVille+" est une ville de "+this.nomPays+", elle comporte : "+this.nbreHabitant+
" => elle est donc de catégorie : "+this.categorie;
}
/**
* Retourne une chaîne de caractères selon le résultat de la comparaison
* @param v1
* objet Ville
* @return comparaison de deux ville
*/
public String comparer(Ville v1){
String str = new String();
if (v1.getNombreHabitant() > this.nbreHabitant)
str = v1.getNom()+" est une ville plus peuplée que "+this.nomVille;
else
str = this.nomVille+" est une ville plus peuplée que "+v1.getNom();
return str;
}
}
|
Vous avez dû remarquer que
l'accesseur de notre variable de classe déclarée privée est aussi déclaré
static,
et ceci est une règle !
Toutes les méthodes de classes n'utilisant que des
variables de classes doivent être déclarées static ! On les appelle des
méthodes de classes car elles sont globales à toutes vos
instances !
Par contre ceci n'est plus vrai si une méthode utilise des
variables d'instances et des variables de classes...
Et maintenant,
si vous testez le code suivant, vous allez voir l'utilité des variables des
classes :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
Ville v = new Ville();
System.out.println("Le nombre d'instances de la classe Ville est : " + Ville.nbreInstance);
System.out.println("Le nombre d'instances de la classe Ville est : " + Ville.getNombreInstanceBis());
Ville v1 = new Ville("marseille", 1236, "france");
System.out.println("Le nombre d'instances de la classe Ville est : " + Ville.nbreInstance);
System.out.println("Le nombre d'instances de la classe Ville est : " + Ville.getNombreInstanceBis());
Ville v2 = new Ville("rio", 321654, "brésil");
System.out.println("Le nombre d'instances de la classe Ville est : " + Ville.nbreInstance);
System.out.println("Le nombre d'instances de la classe Ville est : " + Ville.getNombreInstanceBis());
|
Résultat :
Vous
voyez que le nombre augmente à chaque instanciation ! Et je suppose que le fait
que j'ai utilisé le nom de classe
Ville pour l'appel à nos variables de classes a
dû vous surprendre.

Mais
vous deviez savoir ceci...
Avant la fin de chapitre, j'ai une révélation à vous faire...
Astuce Eclipse
Voici une astuce qui, je pense, va vous simplifier la
vie et quelques minutes de codage...
Je vous vois déjà en train de vous
imaginer coder une classe magnifique avec une centaine de variables d'instances
ainsi qu'une dizaine de variables de classes, et d'un seul coup...
BOUM ! !
Vous
tombez à la renverse lorsque vous devez faire tous les accesseurs de cette
fabuleuse classe !
Voici quelque chose qui va vous plaire.
Eclipse vous fait les
accesseurs automatiquement !
Bon : il faut tout de même
créer toutes les variables au préalable, mais bon...
Nous allons prendre
notre classe comme exemple.
Conservez toutes les déclarations de variables
d'instances et de classes, et effacez tous les accesseurs (mutateurs
compris).
Ensuite, rendez-vous dans le menu
Source et choisissez l'option
Generate
Getters and Setters. Eclipse vous propose alors la liste des variables
présentes dans la classe
Ville.
Ici,
j'ai coché ce qui m'intéressait et j'ai ensuite cliqué sur
OK. Mes accesseurs sont tout prêts !
Alors, j'suis gentil, non ?
Comme vous pouvez le constater, eclispe génère des
accesseurs différents de ceux que nous avions fait aux préalables... Pensez-donc
à mettre à jours votre méthode
main
si vous voulez faire des tests...

Je
profite de cet intermède Eclipse pour vous expliquer pourquoi j'ai mis autant de
commentaires autour de mes variables et de mes méthodes.
Par contre, ceci concerne les personnes qui ont
téléchargé le JDK lors de la première partie !
Comme
je l'ai mentionné dans la première partie, il y a une troisième syntaxe de
commentaires et vous venez de la voir tout au long de ce chapitre. Il s'agit de
commentaires
javadoc !

Cette
syntaxe est quasi identique aux commentaires multilignes à une différence près :
la présence d'une deuxième
* au début
du commentaire.
Vous avez dû constater que la couleur du commentaire était
différente.
- Commentaire standard : commentaire de couleur verte.
- Commentaire javadoc : commentaire de couleur bleue.
À quoi ça sert ?
Tout simplement à documenter
vos programmes et surtout à pouvoir générer une documentation automatiquement.
Dans Eclipse, il vous suffit d'aller dans
Project et
de cliquer sur
Generate Javadoc.
Vous devez avoir
une popup comme ceci :
Si
la commande Javadoc n'est pas renseignée, il vous faut aller chercher, dans le
répertoire
bin de votre JDK, le fichier
javadoc.exe (comme sur la capture d'écran, chez moi c'est dans
C:\SUN\SDK\jdk\bin\javadoc.exe).
Il ne vous reste
plus qu'à cliquer sur
Finish et à tout
approuver.
Vous avez maintenant, dans votre dossier de code source, un
dossier
doc, où se trouve toute la
javadoc de votre projet. Double cliquez sur
index.html dans ce dossier, et voilà !

Pour
les personnes ayant essayé la compilation en ligne de commande, il vous suffit
d'aller dans votre dossier de source Java (là où se trouvent vos fichier
.java) et de taper la commande :
Code :
Bash
Ce qu'il faut retenir
Après tout ceci, un petit topo ne fera pas de
mal...
- Une classe permet de définir des objets. Ceux-ci ont des attributs
(variable(s) d'instances) et des méthodes (méthodes de classes + accesseurs).
- Les objets permettent d'encapsuler du code et des données.
- Une classe, par défaut, est déclarée public.
- Le ou les constructeurs d'une classe doivent
porter le même nom que la classe, et pas de type de retour !
- Il est recommandé de déclarer ses variables d'instance private.
- Dans une classe, on accède aux variables de celle-ci grâce au mot clé
this.
- Une variable de classe est une variable devant être déclarée static.
- Les méthodes n'utilisant que des variables de
classes doivent elles aussi être déclarées static.
- On instancie un nouvel objet grâce au mot clé new.
- L'ordre des paramètres passé dans le constructeur doit être
respecté.
- Eclipse vous aide à développer vos applications.
Voilà !
Vous venez d'apercevoir les méandres de la programmation orientée objet...
Je
suis conscient que ce chapitre fut très riche en nouveautés, vocabulaire,
concepts et méthodologie... mais n'est-ce pas ce que je vous avait dit ?
Maintenant, ce que je vous propose, c'est un petit QCM digestif ! Ça passe
toujours mieux après le digeo !
Vous venez de voir l'un des chapitres les plus
conséquents de cette partie...
Normalement, vous pouvez désormais créer et
gérer des objets...

Mais...
(parce qu'il y a un mais)... vous allez
voir, au fil de la partie suivante, que la programmation orientée objet offre
tout un éventail de possibilités. L'une des plus importantes n'est autre que la
notion d'
héritage.
Sans perdre une seconde, je
propose à ceux qui se sentent d'attaque de passer à la suite !
L'héritage !
Je vous arrête tout de suite... Vous ne toucherez rien !
Pas de rapport d'argent entre nous... Non, la notion d'héritage en
programmation est toute autre, quoique ressemblante à celle que vous connaissez.
C'est l'un des fondements de la programmation orientée objet !
Imaginons
que dans le programme fait précédemment, nous voulions créer un autre type
d'objet :
des objets
Capitale.
Ceux-ci ne seront ni plus ni moins que
des objets
Ville avec un paramètre
en plus... Disons un
président
!
Et donc, au moment de créer votre classe
Capitale, au lieu de tout redéfinir, nous allons
dire que celle-ci est héritée de
Ville. Trêve de bavardage ! À l'assaut !
La notion d'héritage
Comme je vous l'ai déjà dit lors de l'introduction, la
notion d'héritage est l'un des fondements de la programmation orientée objet.
Grâce à elle, nous pourrons créer des classes héritées (appelées aussi
classes dérivées) de nos classes mères (appelées aussi
classes de base).
Nous pourrons créer autant de
classes dérivées, par rapport à notre classe de base, que nous le souhaitons.
Nous pourrons aussi nous servir d'une classe dérivée comme d'une classe de base
pour créer une autre classe dérivée...
Ce que vous devez savoir aussi,
c'est que la notion d'héritage est l'un des piliers de la programmation
événementielle (autre nom de
programmation
graphique). Ceci sera abordé dans la troisième partie de ce tuto.
Pour l'instant, restons dans la programmation procédurale
!
Reprenons l'exemple dont je vous parlais dans l'introduction. Nous
allons donc créer une nouvelle classe, nommée
Capitale héritée de
Ville.
Vous vous rendrez vite
compte que les objets
Capitale auront tous les
attributs et méthodes associés des objets
Ville
!
Code : Java
1
2
3 |
class Capitale extends Ville {
}
|
C'est le mot-clé
extends
qui informe notre application que la classe
Capitale
est héritée de
Ville. Pour vous le prouver, essayez ce
morceau de code dans votre
main :
Code : Java
1
2 |
Capitale cap = new Capitale();
System.out.println("\n\n"+cap.decrisToi());
|
Vous devriez avoir ceci
:
Ceci
est bien la preuve que notre objet
Capitale possède les avantages de notre
objet
Ville. Les objets hérités peuvent accéder
à toutes les méthodes
public de leur classe mère, ici la méthode
decrisToi().
Dans ce cas
rudimentaire, notre objet
Capitale
ne possède que le constructeur par défaut et les méthodes associées.
En
fait, lorsque vous déclarez une classe, si vous ne spécifiez pas de
constructeur, la
JVM créera au moment de l'interprétation le
constructeur par défaut. C'est le cas ici. De plus, notre classe
Capitale hérite de la classe
Ville, ceci a pour effet que le constructeur de
notre objet appelle, de façon tacite, le constructeur de la classe
mère.
C'est pour cela que les variables d'instances ont pu être initialisées
! Par contre, dans notre classe
Capitale, nous ne pouvons pas utiliser
directement les attributs de la classe
Ville.
Essayez ceci dans votre classe :
Code : Java
1
2
3
4
5
6
7 |
public class Capitale extends Ville{
public Capitale(){
this.nomVille = "toto";
}
}
|
Vous allez avoir une belle
erreur de compilation !
Pourquoi ?
Tout simplement parce les
variables de la classe
Ville sont
déclarés
private.
Comme seules les méthodes et
les variables déclarées
public peuvent être utilisées dans une
classe héritée, le compilateur rejette votre demande lorsque vous tentez
d'accéder à des ressources privées d'une classe mère !
Comment y remédier tout en gardant la protection sur
les variables de ma classe mère ?
C'est ici que je vais vous apprendre
un nouveau mot clé :
protected.
En remplaçant la déclaration
des variables et des méthodes privées de la classe
Ville en
protected, cela aura pour effet de
toujours protéger l'accès à celles-ci depuis du code utilisant un objet
Ville ; mais cela permet aux
classes qui héritent de cette dernière d'y avoir accès !
Donc, une fois
toutes les variables et méthodes privées de la classe mère re-déclarées en
protected, notre objet
Capitale aura accès à celles-ci !

Ainsi,
voici votre classe
Ville revue et
corrigée :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221 |
public class Ville {
/**
* Variable publique compteur d'instances
*/
public static int nbreInstance = 0;
/**
* Variable privée compteur d'instances
*/
protected static int nbreInstanceBis = 0;
/**
* Stocke le nom de notre ville
*/
protected String nomVille;
/**
* Stocke le nom du pays de notre ville
*/
protected String nomPays;
/**
* Stocke le nombre d'habitants de notre ville
*/
protected int nbreHabitant;
/**
* Stocke le type de notre ville
*/
protected char categorie;
/**
* Constructeur par défaut
*/
public Ville(){
//On incrémente nos variables à chaque appel au constructeur
nbreInstance++;
nbreInstanceBis++;
nomVille = "Inconnu";
nomPays = "Inconnu";
nbreHabitant = 0;
this.setCategorie();
}
/**
* Constructeur d'initialisation
* @param pNom
* Nom de la Ville
* @param pNbre
* Nombre d'habitants
* @param pPays
* Nom du pays
*/
public Ville(String pNom, int pNbre, String pPays)
{
nbreInstance++;
nbreInstanceBis++;
nomVille = pNom;
nomPays = pPays;
nbreHabitant = pNbre;
this.setCategorie();
}
//*****************************************************************************************
// ACCESSEURS
//*****************************************************************************************
public static int getNombreInstanceBis()
{
return nbreInstanceBis;
}
/**
* Retourne le nom de la ville
* @return le nom de la ville
*/
public String getNom()
{
return nomVille;
}
/**
* Retourne le nom du pays
* @return le nom du pays
*/
public String getNomPays()
{
return nomPays;
}
/**
* Retourne le nombre d'habitants
* @return nombre d'habitants
*/
public int getNombreHabitant()
{
return nbreHabitant;
}
/**
* Retourne la catégorie de la ville
* @return catégorie de la ville
*/
public char getCategorie()
{
return categorie;
}
//*****************************************************************************************
// MUTATEURS
//*****************************************************************************************
/**
* Définit le nom de la ville
* @param pNom
* nom de la ville
*/
public void setNom(String pNom)
{
nomVille = pNom;
}
/**
* Définit le nom du pays
* @param pPays
* nom du pays
*/
public void setNomPays(String pPays)
{
nomPays = pPays;
}
/**
* Définit le nombre d'habitants
* @param nbre
* nombre d'habitants
*/
public void setNombreHabitant(int nbre)
{
nbreHabitant = nbre;
this.setCategorie();
}
//*****************************************************************************************
// METHODES DE CLASSE
//*****************************************************************************************
/**
* Définit la catégorie de la ville
*/
protected void setCategorie() {
if (this.nbreHabitant < 10000000) {
if (this.nbreHabitant < 5000000) {
if (this.nbreHabitant < 1000000) {
if (this.nbreHabitant < 500000) {
if (this.nbreHabitant < 100000) {
if (this.nbreHabitant < 10000) {
if (this.nbreHabitant < 1000) {
if (this.nbreHabitant > 0)
this.categorie = 'A';
else
this.categorie = '?';
} else
this.categorie = 'B';
} else
this.categorie = 'C';
} else
this.categorie = 'D';
} else
this.categorie = 'E';
} else
this.categorie = 'F';
} else
this.categorie = 'G';
} else
this.categorie = 'H';
}
/**
* Retourne la description de la ville
* @return description ville
*/
public String decrisToi(){
return "\t"+this.nomVille+" est une ville de "+this.nomPays+", elle comporte : "+this.nbreHabitant+
" => elle est donc de catégorie : "+this.categorie;
}
/**
* Retourne une chaîne de caractères selon le résultat de la comparaison
* @param v1
* objet Ville
* @return comparaison de deux ville
*/
public String comparer(Ville v1){
String str = new String();
if (v1.getNombreHabitant() > this.nbreHabitant)
str = v1.getNom()+" est une ville plus peuplée que "+this.nomVille;
else
str = this.nomVille+" est une ville plus peuplée que "+v1.getNom();
return str;
}
}
|
Un point important avant
de continuer.
Contrairement au C++, Java ne gère pas
les héritages multiples : une classe dérivée (ou encore classe fille) ne peut hériter que d'une seule classe mère
!
Vous n'aurez donc
JAMAIS ce genre de classe :
Code :
Java
1
2 |
class Toto extends Titi, Tutu{
}
|
À présent, continuons la
construction de notre objet hérité !
Construction d'un objet hérité
Il va de soi que cette opération va se concrétiser avec
nos chers constructeurs.
Notre classe
Ville ne changera plus d'un poil, mais nous
allons par contre agrémenter notre classe
Capitale.
Comme je vous l'avais dit, ce qui
différenciera nos objets
Capitale
de nos objets
Ville sera la
présence d'un champ nouveau : le nom du président. Ce qui signifie que nous
devons créer un constructeur par défaut et un constructeur d'initialisation pour
notre objet
Capitale.
Avant de foncer tête
baissée, il faut que vous sachiez que nous pouvons faire appel aux variables de
la classe mère dans nos constructeurs... Et ceci grâce au mot-clé
super. Ce qui
aura pour effet de récupérer les éléments de l'objet de base, et de les envoyer
à notre objet hérité.
Démonstration :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13 |
class Capitale extends Ville {
private String president;
/**
*Constructeur par défaut
*/
public Capitale(){
//Ce mot clé appelle le constructeur de la classe mère.
super();
president = "aucun";
}
}
|
Si vous testez à nouveau le
petit exemple que je vous avais montré un peu plus haut, vous vous apercevrez
que le constructeur par défaut fonctionne toujours... Et pour cause, car ici,
super()
appelle le constructeur par défaut de l'objet
Ville
dans notre constructeur de
Capitale, puis nous avons
rajouté le président par défaut.
Mais la méthode
decrisToi() ne prend pas en compte le nom du
président...
Eh bien le mot-clé
super() fonctionne aussi pour les méthodes
de classe. Ce qui nous donne une méthode
decrisToi() un peu différente... car nous allons
rajouter le champ président dans notre description.
Voyez plutôt
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 |
class Capitale extends Ville {
private String president;
/**
*Constructeur par défaut
*/
public Capitale(){
//Ce mot clé appel le constructeur de la classe mère.
super();
president = "aucun";
}
/**
*Description d'une capitale
*/
public String decrisToi(){
String str = super.decrisToi() + "\n \t ==>>" + this.president + " est son président";
//Pour bien vous montrer, j'ai ajouté la ligne ci-dessous, mais vous n'êtes pas obligés...
System.out.println("Invocation de super.decrisToi()");
System.out.println(super.decrisToi());
return str;
}
}
|
Si vous relancez les mêmes
instructions présentes dans le
main, depuis le
début, vous aurez quelque chose comme ça :
Il
y a du mieux, non ?
Bon, d'accord, nous n'avons toujours pas fait le constructeur
d'initialisation de
Capitale... Eh bien ? Qu'est-ce
que nous attendons ?
Code complet de notre classe
CapitaleCode :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 |
public class Capitale extends Ville {
private String president;
/**
*Constructeur par défaut
*/
public Capitale(){
//Ce mot clé appelle le constructeur de la classe mère.
super();
president = "aucun";
}
/**
* Constructeur d'initialisation de capitale
*/
public Capitale(String nom, int hab, String pays, String president){
super(nom, hab, pays);
this.president = president;
}
/**
*Description d'une capitale
*/
public String decrisToi(){
String str = super.decrisToi() + "\n \t ==>>" + this.president + " est son président";
return str;
}
/**
* @return le nom du président
*/
public String getPresident() {
return president;
}
/**
* Définit le nom du président
* @param president
*/
public void setPresident(String president) {
this.president = president;
}
}
|
Donc : dans le constructeur
d'initialisation de notre
Capitale, vous remarquez
la présence de
super(nom, hab,
pays);. Difficile de ne pas le voir...
Ici, cette ligne de
code joue le même rôle que pour le constructeur par défaut. Sauf qu'ici, le
constructeur auquel
super fait référence prend trois
paramètres... donc
super doit prendre ces
paramètres.
Si vous ne lui mettez aucun paramètre, super() renverra le constructeur par défaut de la classe
Ville...
Testez ce code :
Code : Java
1
2 |
Capitale cap = new Capitale("Paris", 654987, "France", "Sarko");
System.out.println("\n"+cap.decrisToi());
|
Vous devriez voir apparaître
sous vos yeux ébahis :

:
Je
vais encore vous interpeler mais... ce que vous venez de faire sur la méthode
decrisToi() s'appelle :
une méthode polymorphe, ce qui nous
conduit tout de suite à la suite

!
Le polymorphisme
Voici encore un des concepts fondamentaux de la
programmation orientée objet :
Le
polymorphisme.
Ce concept complète parfaitement celui de
l'héritage.
Comme vous l'avez vu, le polymorphisme n'est pas si
compliqué qu'il pourrait sembler l'être !
Nous pouvons le caractériser en disant qu'il permet
de manipuler des objets sans vraiment connaître leur type.
Dans
notre exemple, vous avez vu qu'il suffisait d'utiliser la méthode
decrisToi() sur un objet
Ville ou sur un objet
Capitale, et cela sans se soucier de leur type.
On pourrait construire un tableau d'objets, et appeler la
decrisToi() sans se soucier de son contenu :
villes, capitales, ou les deux.
D'ailleurs nous allons le faire. Essayez
ce code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 |
//Def d'un tableau de ville null
Ville[] tableau = new Ville[6];
//Définition d'un tableau de noms de Villes et d'un tableau de nombres d'habitants
String[] tab = {"Marseille", "lille", "caen", "lyon", "paris", "nantes"};
int[] tab2 = {123456, 78456, 654987, 75832165, 1594,213};
/* Les 3 premiers éléments du tableau seront des Villes,
et le reste, des capitales
*/
for(int i = 0; i < 6; i++){
if (i <3){
Ville V = new Ville(tab[i], tab2[i], "france" );
tableau[i] = V;
}
else{
Capitale C = new Capitale(tab[i], tab2[i], "france", "Sarko");
tableau[i] = C;
}
}
//il ne nous reste plus qu'à décrire tout notre tableau !
for(Ville v : tableau){
System.out.println(v.decrisToi()+"\n");
}
|
Résultat :
Une
petite nouveauté, la création d'un tableau d'un certain nombre d'entrées
vides. Rien de bien compliqué à cela, vous voyez que la syntaxe
est toute simple.
Nous créons un tableau de villes, avec des villes et des
capitales (nous avons le droit de faire ça, car les objets
Capitale sont aussi des objets
Ville...

), dans notre première boucle
for.
Dans la seconde, nous affichons la
description de ces objets... et vous voyez que la méthode
polymorphe decrisToi() fait bien son travail !
Dans ta boucle, tu n'utilises que des objets
Ville.
Tout à fait. On
appelle ceci la
covariance des
variables !
Cela signifie qu'une variable objet peut contenir
un objet qui hérite du type de cette variable. Dans notre cas, un objet de type
Ville peut contenir un objet de
type
Capitale. Dans ce cas, on dit
que
Ville est la
super
classe par rapport à
Capitale.
La covariance est efficace dans le
cas où la classe héritant redéfinit certaines des méthodes de sa super
classe.
Attention à ne pas confondre la surcharge de
méthode avec une méthode polymorphe.
Pour
dire les choses simplement :
- une méthode surchargée a des paramètres que la méthode de base n'a pas, ou
a le même nombre de paramètres, mais de types différents ;
- une méthode polymorphe a un squelette identique à celle de base, mais un
traitement différent. Celle-ci fait référence à une autre classe et donc, par
extension, à une autre instance de cette classe. On
peut dire que les méthodes polymorphes sont typiques des classes héritées
!
Vous devez savoir encore une chose sur
l'héritage. Lorsque vous créez une classe (
Ville par
exemple), celle-ci est une classe héritée de la classe
Object présente dans Java.
Cette écriture est donc
tout à fait correcte :
Code : Java
1
2
3 |
class Ville extends Object{
..........
}
|
Toutes nos classes héritent donc
des méthodes de la classe
Object, comme
equals(), qui prend un objet en
paramètre, et qui permet de tester l'égalité d'objets. Vous vous en êtes
d'ailleurs servis pour tester l'égalité de
String()
dans la première partie de ce tuto.
Donc, si nous redéfinissons une méthode
de la classe
Object dans la classe
Ville, nous pourrions utiliser la
covariance.
La méthode de la classe
Object qui est le plus souvent redéfinie est la
méthode
toString(), qui retourne un
String et
qui a pour rôle de décrire l'objet en question (tout comme notre méthode
decrisToi()). Nous allons donc
faire un copier / coller de notre procédure de la méthode
decrisToi() dans une nouvelle méthode de la
classe
Ville :
toString().
Voici :
Code : Java
1
2
3
4 |
public String toString(){
return "\t"+this.nomVille+" est une ville de "+this.nomPays+", elle comporte : "+this.nbreHabitant+
" => elle est donc de catégorie : "+this.categorie;
}
|
Nous faisons de même dans la
classe
Capitale :
Code : Java
1
2
3
4 |
public String toString(){
String str = super.decrisToi() + "\n \t ==>>" + this.president + " est son président";
return str;
}
|
Maintenant, testez ce code
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 |
//Def d'un tableau de ville null
Ville[] tableau = new Ville[6];
//Définition d'un tableau de noms de Villes et d'un tableau de nombres d'habitants
String[] tab = {"Marseille", "lille", "caen", "lyon", "paris", "nantes"};
int[] tab2 = {123456, 78456, 654987, 75832165, 1594,213};
/* Les 3 premiers éléments du tableau seront des Villes
et le reste des capitales
*/
for(int i = 0; i < 6; i++){
if (i <3){
Ville V = new Ville(tab[i], tab2[i], "france" );
tableau[i] = V;
}
else{
Capitale C = new Capitale(tab[i], tab2[i], "france", "Sarko");
tableau[i] = C;
}
}
//il ne nous reste plus qu'à décrire tout notre tableau !
for(Object obj : tableau){
System.out.println(obj.toString()+"\n");
}
|
Vous pouvez constater qu'il fait
exactement la même chose que le précédent ; nous n'avons pas à nous soucier du
type d'objet pour afficher sa description. Je pense que vous commencez à
apercevoir la puissance de Java !
ATTENTION : si vous ne redéfinissez
pas ou ne polymorphez pas une méthode d'une classe mère dans une classe fille
(exemple de toString()), à l'appel
de celle-ci avec un objet fille, c'est la méthode de la classe mère qui sera
invoquée ! !
Une précision : si vous avez un objet
v de type
Ville par exemple, que vous n'avez pas redéfini
la méthode
toString() et que vous
testez ce code :
Code : Java
Cette instruction appelle
automatiquement la méthode
toString() de la classe
Object ! Mais vu que
vous avez redéfini la méthode
toString() dans votre classe
Ville, ces deux
instructions sont équivalentes :
Code : Java
1
2
3 |
System.out.println(v.toString());
//Est équivalent à
System.out.println(v);
|
Pour plus de clarté, je
conserverai la première syntaxe ! Mais vous devez savoir ceci !
En clair,
vous avez accès aux méthodes
public et
protected de la classe
Object dès que vous
créez une classe objet (héritage tacite).
Vous pouvez donc utiliser les dites
méthodes ; mais si vous ne les redéfinissez pas... l'invocation se fera sur la
classe mère avec les traitements de la classe mère.
Si vous voulez un bel
exemple de ce que je viens de vous dire, vous n'avez qu'à retirer la
redéfinition de la méthode
toString() dans les classes
Ville et
Capitale : vous verrez que le code de la méthode
main
fonctionne toujours, mais le résultat n'est plus du tout le même car, à l'appel
de la méthode
toString(), la JVM va regarder si celle-ci
existe dans la classe appelante et, si elle ne la trouve pas, elle remonte dans
la hiérarchie jusqu'à arriver à la classe
Object...
Attention 2 : ce code fonctionne
bien mais, si vous remplacez la méthode
toString() par la méthode
decrisToi(), le programme ne fonctionne plus...

Et cela pour une bonne raison : la méthode
decrisToi() n'existe pas dans la classe
Object.
Vous devez savoir qu'une méthode est invoquable par
un objet QUE si celui-ci définit la dite méthode !
Donc, ce code ne
fonctionne pas :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 |
public class Sdz1 {
public static void main(String[] args){
//Def d'un tableau de ville null
Ville[] tableau = new Ville[6];
//Définition d'un tableau de noms de Villes et d'un tableau de nombres d'habitants
String[] tab = {"Marseille", "lille", "caen", "lyon", "paris", "nantes"};
int[] tab2 = {123456, 78456, 654987, 75832165, 1594,213};
/* Les 3 premiers éléments du tableau seront des Villes,
et le reste, des capitales
*/
for(int i = 0; i < 6; i++){
if (i <3){
Ville V = new Ville(tab[i], tab2[i], "france" );
tableau[i] = V;
}
else{
Capitale C = new Capitale(tab[i], tab2[i], "france", "Sarko");
tableau[i] = C;
}
}
//il ne nous reste plus qu'à décrire tout notre tableau !
for(Object v : tableau){
System.out.println(v.decrisToi()+"\n");
}
}
}
|
Pour que cela fonctionne,
vous devez dire à la JVM que la référence de type
Object est en fait une référence de type
Ville. Comme ceci :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 |
public class Sdz1 {
public static void main(String[] args){
//Def d'un tableau de ville null
Ville[] tableau = new Ville[6];
//Définition d'un tableau de noms de Villes et d'un tableau de nombres d'habitants
String[] tab = {"Marseille", "lille", "caen", "lyon", "paris", "nantes"};
int[] tab2 = {123456, 78456, 654987, 75832165, 1594,213};
/* Les 3 premiers éléments du tableau seront des Villes,
et le reste, des capitales
*/
for(int i = 0; i < 6; i++){
if (i <3){
Ville V = new Ville(tab[i], tab2[i], "france" );
tableau[i] = V;
}
else{
Capitale C = new Capitale(tab[i], tab2[i], "france", "Sarko");
tableau[i] = C;
}
}
//il ne nous reste plus qu'à décrire tout notre tableau !
for(Object v : tableau){
System.out.println(((Ville)v).decrisToi()+"\n");
}
}
}
|
Vous transtypez la référence
v en
Ville par cette syntaxe :
Code : Java
Ici, l'ordre des opérations
s'effectue comme ceci :
- vous transtypez la référence v en Ville
- vous appliquez la méthode decrisToi() à la référence appelante, ici, une
référence Object changée en
Ville.
Vous voyez
donc l'intérêt des méthodes polymorphes. Avec celles-ci, vous n'avez plus à vous
soucier du type de variable appelante ; cependant, n'utilisez le type
Object
qu'avec parcimonie.
Il existe encore un type de méthode dont je ne vous
ai pas encore parlé. Il s'agit des méthodes dites
final. Ces méthodes
sont figées et vous ne pourrez
JAMAIS
redéfinir une méthode déclarée
final. Un exemple de ce type de méthode
est la méthode
getClass() de la classe
Object : vous ne
pourrez pas redéfinir cette méthode et heureusement, car celle-ci retourne un
objet
Capitale dans le fonctionnement de Java
(nous verrons cela plus tard).
Il existe aussi des classes déclarées final. Vous avez compris que ces classes sont
immuables... Et vous ne pouvez donc pas faire hériter un objet d'une classe
déclarée final !
Ce qu'il faut retenir
Encore un petit topo des familles. Je pense qu'avec ce
genre de chapitre, ce n'est pas du luxe...
- Une classe hérite d'une autre classe par le biais du mot clé extends.
- Une classe ne peut hériter que d'une seule et
unique classe !
- Si nous ne définissons pas de constructeur dans nos classes, la
JVM se charge d'en créer un à l'interprétation.
- Si aucun constructeur n'est défini dans une classe fille, la JVM
en créera un et appellera automatiquement le constructeur de la classe
mère.
- La classe fille hérite de toutes les propriétés et méthodes public et
protected de la classe mère.
- Les méthodes et propriétés private d'une classe mère ne sont pas
accessibles dans la classe fille.
- On peut redéfinir (changer tout le code) d'une méthode héritée.
- On peut utiliser le polymorphisme sur une méthode par le biais du mot clé
super.
- Le polymorphisme permet, entre autres, d'ajouter des traitements
spécifiques à une méthode en continuant d'utiliser les traitements déjà
définis (utilisation de super).
- Grâce à l'héritage et au polymorphisme, nous pouvons utiliser la
covariance des variables !
- Si une méthode d'une classe mère n'est pas redéfinie ou polymorphée, à
l'appel de cette méthode par le biais d'un objet enfant, c'est la
méthode de la classe mère qui sera appelée !
- Vous ne pouvez pas hériter d'une classe déclarée final.
- Une méthode déclarée final est non
redéfinissable.
Je crois que vous êtes prêts
pour un petit QCM... Mais prenez le temps de digérer tout ceci !
Faites des
essais, testez, comparez, c'est le meilleur moyen de bien comprendre les choses.
Si vous êtes prêts pour la suite, allons tout de suite
voir ce qui se passe : Apprendre à
modéliser.
Apprendre à modéliser
Dans ce chapitre nous allons voir le principe de modélisation
d'objet.
Ne vous leurrez pas, ça sera assez indigeste, mais faites-moi
confiance...

UML, mais qu'est-ce donc ?
UML est le sigle signifiant
Unified
Modeling
Language : traduisez par "
langage de modélisation unifié".
Il ne s'agit pas d'un
langage de programmation mais plutôt d'une méthodologie de modélisation comme la
méthode
merise,
etc.
À quoi ça sert ?
Je sais que vous êtes des
Zér0s avertis en matière de programmation, ainsi qu'en informatique en général,
mais mettez-vous dans la peau d'une personne totalement dénuée de toute
connaissance dans le domaine.

Il
fallait trouver un langage commun entre les commerciaux, les responsables de
projets informatiques, les développeurs afin que tout ce petit monde se
comprenne. Avec UML, c'est le cas.
En fait, avec UML, vous pouvez
modéliser toutes les parties de développement d'une application informatique, de
sa conception à la mise en route, tout ceci grâce à des diagrammes. Il est vrai
que certains de ces diagrammes sont plus adaptés à des informaticiens, mais il
en existe qui permettent de voir comment interagit l'application dans son
contexte de fonctionnement... Et dans ce genre de cas, la connaissance de
l'entreprise pour laquelle l'application est prévue est de mise. On utilise donc
un mode de communication commun à tout le monde : UML.
Il existe bien sûr
des outils de modélisation afin de créer de tels diagrammes. Personnellement,
j'utilise
argoUML mais il en existe
d'autres, comme :
- boUML
- Together
- Poseidon
- Pyut
- ...
ArgoUML a le mérite d'être gratuit et fait en Java...
donc multi-plates-formes.

Avec
ces outils, vous pouvez réaliser les différents types de diagrammes qu'UML vous
propose :
- diagramme de use case (cas
d'utilisation) : permet de déterminer les différents cas d'utilisation d'un
programme informatique ;
- diagramme de classes : celui dont nous allons nous servir ; permet de
modéliser des classes ainsi que les interactions entre elles ;
- des diagrammes de séquences : permettent de visualiser le déroulement
d'une application dans un contexte donné ;
- et d'autres encore...
Voici un exemple de diagramme de
classe :
Vous
avez dû remarquer qu'il s'agissait des classes que nous avons utilisées lors des
chapitres précédents. Je ne vous cache pas non plus qu'il s'agit d'une version
simplifiée... En effet, vous pouvez constater que je n'ai pas mis toutes les
méthodes déclarées
public de la
classe
Object ainsi que des classes que nous
avons codées.
Je ne vais pas vous apprendre à utiliser argoUML non plus, mais
plutôt à savoir lire un diagramme car, dans certains cas, il s'avère pratique de
modéliser les classes et l'interaction entres celles-ci. Ne serait-ce que pour
avoir plus de recul sur notre travail. Mais aussi parce qu'il y a des concepts
de programmation qu'il est plus facile d'expliquer avec un diagramme qu'avec de
longs discours...
Modéliser un objet
À présent, nous allons apprendre à lire un diagramme de
classes.
Vous avez deviné qu'une classe est modélisée sous cette forme :
Voici
une classe nommé
ObjetA qui a comme
attributs :
- numero de type int
- nom de type String
- et bool de type boolean.
Et comme méthodes :
- getNom() qui retourne une chaîne de caractères
- setNom() qui ne renvoie rien
- afficher() qui renvoie elle aussi une chaîne
de caractères.
La portée des attributs et des méthodes n'est pas
modélisée ici...
Vous voyez que la modélisation d'un objet est toute simple
et très compréhensible !

Maintenant,
voyons les interactions entre objets.
Modéliser les interactions entre objets
Vous allez voir que les interactions sont aussi très
simples à modéliser.
En fait, comme vous avez pu le voir sur l'exemple, les
interactions sont modélisées par des flèches de types différents. Nous allons
voir maintenant celles que nous pouvons d'ores et déjà utiliser, dans l'état
actuel de nos connaissances (au fur et à mesure, nous verrons d'autres
flèches).
Regardez ceci :
Sur
ce diagramme, vous pouvez voir un deuxième objet qui a lui aussi des paramètres.
Mais ne vous y trompez pas,
ObjetB
possède aussi les attributs et les méthodes de la classe
ObjectA. Et d'après vous, pourquoi ?
Car la
flèche qui relie nos deux objets signifie "
extends". En gros, vous pouvez lire ce
diagramme comme suit :
l'ObjetB
hérite de l'ObjetA, ou encore ObjetB est
un objetA.
Nous allons voir une autre
flèche d'interaction. Je sais que nous n'avons pas encore vu ce cas de figure,
mais il est simple à comprendre.
Comme nous pouvons mettre des objets de type
String
dans des classes que nous développons, nous pouvons aussi mettre comme variable
d'instance, ou de classe, un objet que nous avons codé. Voici un diagramme
modélisant ce cas de figure :
Dans
cet exemple simpliste, vous voyez que nous avons toujours notre héritage entre
un objet A et un objet B mais dans ce cas, l'
ObjetA (et donc l'
ObjetB) ont une variable de classe de type
ObjetC, ainsi qu'une méthode ayant
un type de retour
ObjetC (car la
méthode va retourner un
ObjetC).
Vous pouvez lire ce diagramme comme
suit :
l'ObjetA
a un ObjetC.
Ici, il n'y a qu'un seul
objetC : "a
UN".
Voici le code Java correspondant à ce
diagramme.
Fichier ObjetA.java
Code :
Java
1
2
3
4
5
6
7 |
public class ObjetA{
protected ObjetC obj = new ObjetC();
public ObjetC getObject(){
return obj;
}
}
|
Fichier ObjetB.java
Code :
Java
1
2
3 |
public class ObjetB extends ObjetA{
}
|
Fichier ObjetC.java
Code :
Java
Il y a encore une dernière
flèche que nous pouvons voir car
il ne
diffère que légèrement de la première.
Voici un diagramme la
mettant en oeuvre :
Nous
avons ici le même diagramme que précédemment, à l'exception de l'
ObjetD. Ici, nous devons lire le diagramme comme
suit :
l'ObjetA
est composé d'ObjetD.
Ici, il y aura plusieurs d'instances
d'ObjetD dans ObjetA.
Vous pouvez d'ailleurs
remarquer que la variable d'instance correspondante est de type tableau...
Voici le code Java correspondant :
Fichier ObjetA.java
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12 |
public class ObjetA{
protected ObjetC obj = new ObjetC();
protected ObjetD[] objD = new ObjetD[10];
public ObjetC getObject(){
return obj;
}
public ObjectD[] getObjectD(){
return objD;
}
}
|
Fichier ObjetB.java
Code :
Java
1
2
3 |
public class ObjetB extends ObjetA{
}
|
Fichier ObjetC.java
Code :
Java
Fichier ObjetD.java
Code :
Java
Voilà,
c'en est fini pour le moment. Attendez-vous donc à avoir des diagrammes dans vos
prochains chapitres...

Il
n'y aura pas de QCM car j'estime qu'il n'y a rien de difficile ici.
Après ce que nous avons vu au cours de ce chapitre et
des précédents, nous allons tout de suite voir les classes abstraites !
Les classes abstraites
Nous voici de retour dans l'un des fondements du langage
Java.
En effet, vous verrez ici que les classes abstraites sont vraiment
pratiques.
Sans plus attendre : let's go !
Qu'est-ce c'est ?
Ne vous en faites pas... Ici
abstraite n'a rien de commun avec un
certain mouvement de peinture.

En
fait, une classe
abstraite est
quasiment comme une classe normale. Oui, comme une classe que vous avez
maintenant l'habitude de coder.
Ceci dit, elle a tout de même une
particularité :
vous ne pouvez
pas l'instancier !
Vous avez bien entendu

. Imaginons que nous ayons une classe
A déclarée
abstraite. Ce code ne
compilera pas :
Code : Java
1
2
3
4
5
6
7
8 |
public class Test{
public static void main(String[] args){
A obj = new A();//Erreur de compilation ! !
}
}
|
À quoi ça sert, alors ?
J'attendais cette
question... C'est pour ça que je n'ai pas commencé directement par un exemple de
classe abstraite.
Tout d'abord, je vais vous donner un exemple de situation
(de programme, en fait).
Imaginez que vous êtes en train de réaliser un
programme qui gère différents types d'animaux (oui, je sais : l'exemple est
bête, mais il a le mérite d'être simple à comprendre).
Dans ce programme,
vous avez :
- des loups
- des chiens
- des chats
- des lions
- des tigres.
Je pense tout de même que vous n'allez pas
faire toutes vos classes bêtement... il va de soi que tous ces animaux ont des
choses en commun ! Et qui dit chose en commun... dit héritage.
Que
pouvons-nous définir de commun à tous ces animaux, sinon :
- une couleur
- un poids
- qu'ils crient
- qu'ils se déplacent
- qu'ils mangent
- qu'ils boivent.
Nous pouvons donc faire une classe mère,
appelons-la
Animal.
Avec ce que
nous avons dégagé de commun, nous pouvons lui définir des attributs et des
méthodes. Voici donc à quoi pourraient ressembler nos classes pour le moment
:
Nous avons bien notre classe mère
Animal et nos animaux qui en héritent.
À
présent, laissez-moi vous poser une question.
Vu que notre classe Animal est public
dans notre cas, qu'est-ce qu'est censé faire un objet Animal ? Quel est son poids, sa couleur, que
mange-t-il ?
Si nous avons un morceau de code qui ressemble à ceci :
Code : Java
1
2
3
4
5
6 |
public class Test{
public static void main(String[] args){
Animal ani = new Animal();
ani.manger();//Que doit-il faire ? ?
}
}
|
...personnellement, je ne sais
pas comment mange un objet
Animal...
Vous conviendrez que toutes
les classes ne sont pas bonnes à être instanciées !C'est là que
rentrent en jeu nos classes abstraites. En fait, ces classes servent à définir
une
super classe.
D'accord, mais comment on empêche une classe d'être
instanciable puisque tu nous a dit que la JVM déclare un constructeur par
défaut... On ne peut donc pas omettre le constructeur !
Tout à fait.
Pour répondre à cette question : suivez le guide !
Une classe Animal très abstraite
En fait, il existe une règle pour qu'une classe soit
considérée comme abstraite. Elle doit être déclarée avec le mot clé
abstract.
Voici un exemple
illustrant mes dires :
Classe déclarée abstraite :
Code : Java
Une telle classe peut avoir
le même contenu qu'une classe normale. Ses enfants pourront utiliser tous ses
éléments déclarés (attributs et méthodes)
public. Cependant, ce type de classe
permet de définir des méthodes abstraites. Ces méthodes ont une particularité ;
elle n'ont pas de corps !

En
voici un exmple :
Code : Java
1
2
3 |
abstract class Animal{
abstract void manger();//une méthode abstraite
}
|
Vous voyez pourquoi on dit
"
méthode abstraite", difficile de
voir ce que cette méthode sait faire...
Retenez bien qu'une méthode abstraite n'est
composée que de l'entête de la méthode suivie d'un point-virgule : ;
Jusque-là ça va, mais concrètement, à quoi ça sert
?
Tout d'abord, vous devez savoir qu'une méthode abstraite ne peut
exister que dans une classe abstraite. Si dans une classe, vous avez une méthode
déclarée abstraite, vous
DEVEZ DÉCLARER CETTE CLASSE
COMME ETANT ABSTRAITE.
Maintenant voyons à quoi cela peut
servir. Vous avez vu les avantages de l'héritage et du polymorphisme. Dans ce
cas, nos classes enfants hériteront aussi des classes abstraites mais, vu que
celles-ci n'ont pas de corps, nos classes enfants seront
OBLIGÉES de redéfinir ces méthodes !De ce fait,
nos classes enfants auront des méthodes polymorphes en leur sein et donc, la
covariance des variables repointe le bout de son nez...

La
covariance appliquée aux classes abstraites donne ceci :
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
public class Test{
public static void main(String args[]){
Animal loup = new Loup();
Animal chien = new Chien();
loup.manger();
chien.crier();
}
}
|
Attends ! Tu nous as dit qu'on ne pouvait instancier
de classe abstraites ?
Et je maintiens mes dires. Ici, nous n'avons pas
instancié notre classe abstraite. Nous avons instancié un objet
Loup que nous avons mis dans un objet de type
Animal : il en va de même pour
l'instanciation de la classe
Chien.
Vous devez vous rappeler que l'instance se crée avec
le mot clé new. En aucun cas le fait de déclarer une
variable d'un type de classe donnée (ici, Animal)
est une instanciation ! Ici nous instancions un Loup et un Chien.
Vous pouvez aussi utiliser
une variable de type
Object comme référence pour un objet
Loup, un objet
Chien...
Vous savez déjà que ce code
fonctionne :
Code : Java
1
2
3
4
5
6 |
public class Test{
public static void main(String[] args){
Object obj = new Loup();
obj.manger();
}
}
|
Par contre, ceci pose
problème :
Code : Java
1
2
3
4
5
6 |
public class Test{
public static void main(String[] args){
Object obj = new Loup();
Loup l = obj;//Problème de référence
}
}
|
Eh oui ! Vous essayez ici de
mettre une référence de type
Object dans une référence de type
Loup . Pour avertir la JVM que la
référence que vous voulez mettre dans votre objet de type
Loup est un
Loup, vous devez utiliser le transtypage
!
Revoyons notre code :
Code : Java
1
2
3
4
5
6 |
public class Test{
public static void main(String[] args){
Object obj = new Loup();
Loup l = (Loup)obj;//Vous prévenez la JVM que la référence que vous passez est de type Loup
}
}
|
Vous pourrez bien évidemment
instancier directement un objet
Loup, un objet
Chien ou tout autre.
À
présent, étoffons nos classes et notre diagramme avant d'avoir un code qui
fonctionne bien !
Étoffons notre exemple
Nous allons donc rajouter des morceaux de code à nos
classes.
Tout d'abord, voyons le topo de ce que nous savons.
- Nos objets auront tous une couleur et un poids différents. Nos classes
auront donc le droit de modifier ceux-ci.
- Ici, nous partions du principe que tous nos animaux mangeront de la
viande. La méthode manger() sera
donc définie dans la classe Animal.
- Idem pour la méthode boire().
Ils boiront tous de l'eau
.
- Par contre, ils ne crient pas et ne se déplaceront pas de la même manière.
Nous ferons donc des méthodes polymorphes et déclarerons les méthodes
crier() et deplacement() abstraites dans la classe Animal.
Voici ce que donneraient nos
classes :
J'ai
colorié la classe abstraite en bleu mais il y un autre moyen de savoir si une
classe est abstraite :
le nom de celle-ci est en
italique.Je ne sais pas si c'est une convention ou non, mais
argoUML la différencie de cette façon !

Nous
voyons bien que notre classe
Animal est déclarée
abstraite et que nos classes filles héritent de celle-ci. De plus, nos classes
filles ne redéfinissent que deux méthodes sur quatre, on en conclut ici que ces
deux méthodes doivent être abstraites.
Nous ajouterons deux constructeurs à
nos classes filles, un par défaut, ainsi qu'un avec les deux paramètres
d'initialisation. À ceci nous ajouterons aussi les accesseurs d'usage.
Cependant... nous pouvons améliorer un peu cette architecture, sans pour autant
rentrer dans les détails !
Vu les animaux présents, nous aurions pu faire
une sous-classe
Carnivore, ou
encore
AnimalDomestique et
AnimalSauvage... Ici, nous allons
nous contenter de faire deux sous-classes
Canin et
Felin qui hériteront d'
Animal et dont nos objets hériteront !

Nous
allons redéfinir la méthode
deplacement() dans cette classe car nous allons
partir du principe que les félins se déplacent d'une certaine façon, et les
canins d'une autre. Avec cet exemple, nous réviserons le polymorphisme...

Voilà
notre diagramme mis à jour :
Vous
avez vu ? J'ai ajouté une méthode
toString()
:D.
Voici les codes Java correspondant :
Animal.java
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 |
abstract class Animal {
/**
* La couleur de l'animal
*/
protected String couleur;
/**
* Le poids
*/
protected int poids;
/**
* La méthode manger
*/
protected void manger(){
System.out.println("Je mange de la viande");
}
/**
* La méthode boire
*/
protected void boire(){
System.out.println("Je bois de l'eau !");
}
/**
* La méthode de déplacement
*/
abstract void deplacement();
/**
* La méthode de cri
*/
abstract void crier();
public String toString(){
String str = "Je suis un objet de la " + this.getClass() + ", je suis " + this.couleur + ", je pèse " + this.poids;
return str;
}
}
|
Felin.java
Code :
Java
1
2
3
4
5
6
7
8 |
public abstract class Felin extends Animal {
@Override
void deplacement() {
System.out.println("Je me déplace seul !");
}
}
|
Canin.java
Code :
Java
1
2
3
4
5
6
7
8 |
public abstract class Canin extends Animal {
@Override
void deplacement() {
System.out.println("Je me déplace en meute !");
}
}
|
Chien.java
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
public class Chien extends Canin {
public Chien(){
}
public Chien(String couleur, int poids){
this.couleur = couleur;
this.poids = poids;
}
void crier() {
System.out.println("J'aboie sans raison ! ");
}
}
|
Loup.java
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
public class Loup extends Canin {
public Loup(){
}
public Loup(String couleur, int poids){
this.couleur = couleur;
this.poids = poids;
}
void crier() {
System.out.println("J'hurle à la lune en faisant ouhouh ! ! ");
}
}
|
Lion.java
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
public class Lion extends Felin {
public Lion(){
}
public Lion(String couleur, int poids){
this.couleur = couleur;
this.poids = poids;
}
void crier() {
System.out.println("Je rugis dans la savane !");
}
}
|
Tigre.java
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
public class Tigre extends Felin {
public Tigre(){
}
public Tigre(String couleur, int poids){
this.couleur = couleur;
this.poids = poids;
}
void crier() {
System.out.println("Je grogne très fort !");
}
}
|
Chat.java
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
public class Chat extends Felin {
public Chat(){
}
public Chat(String couleur, int poids){
this.couleur = couleur;
this.poids = poids;
}
void crier() {
System.out.println("Je miaule sur les toits !");
}
}
|
Dis donc ! Une classe abstraite ne doit pas avoir une
méthode abstraite ?
Je n'ai jamais dis ça ! Une classe déclarée
abstraite n'est plus instanciable, mais elle n'est nullement obligée d'avoir des
méthodes abstraites !
En revanche, une classe ayant
une méthode abstraite doit être déclarée abstraite !
Maintenant que vous avez toutes vos classes,
faites
des tests. Autant que vous le voulez.
Dans cet esprit :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
Loup l = new Loup("Gris bleuté", 20);
l.boire();
l.manger();
l.deplacement();
l.crier();
System.out.println(l.toString());
}
}
|
Voilà le jeu d'essai de ce
code :
Dans la méthode toString() de la classe Animal, j'ai utilisé la méthode getClass() qui -je vous le donne en mille- est dans la classe
Object. Celle-ci retourne "class <nom de la classe>".
Dans cet
exemple, nous pouvons voir que nous avons un objet
Loup.
- À l'appel de la méthode boire() : l'objet appelle la méthode de la
classe Animal.
- À l'appel de la méthode manger() : idem.
- À l'appel de la méthode toString() : idem.
- À l'appel de la methode deplacement() : c'est la méthode de la classe
Canin qui est invoquée ici.
- À l'appel de la méthode crier() : c'est la méthode de la classe
Loup qui est appelée.
Remplacez le type de référence (ici,
Loup) par
Animal ou
Object. Essayez avec des objets
Chien, etc. Et vous verrez que tout
fonctionne, excepté que vous ne pourrez pas instancier d'
Object, de
Felin ou de
Canin !
Avant de partir en quête
d'un QCM, je crois qu'une nouvelle astuce d'eclispe est disponible !
Astuce d'Eclipse
Lorsque vous créez votre classe et plus
particulièrement vos classes héritées, Eclipse peut gérer la gestion des mots
clé.
Il peut :
- ajouter le mot clé extends avec la classe mère
- ajouter les méthodes à redéfinir dans le cas d'une classe abstraite.
Voyez comment faire. Nous allons faire une classe
Panthere héritée de
Felin.
Une fois que vous avez inscrit le nom
de votre classe, regardez plus bas : vous pouvez voir le nom de la super classe
de votre nouvelle classe :
En
cochant la case
abstract, votre nouvelle classe sera
abstraite. Et, comme vous pouvez le voir, votre classe hérite par défaut de
Object. Pour changer ceci, cliquez
sur
Browse. Vous arrivez sur cette fenêtre ; ici, il
vous suffit de mettre le nom de la classe mère, comme ceci :
Vous
devez taper le nom complet de votre classe. Ici, comme nous n'avons pas encore
utilisé de
package, il suffit de
taper
Felin. En plus, Eclipse gère
l'auto-complétion, ce qui signifie qu'il termine tous les noms des classes en
vous proposant une liste exhaustive. Il vous suffit donc de choisir la classe
mère, et de valider.
Lorsque la classe mère est dans un package, vous
devez saisir le nom complet de la classe. Par exemple, si vous voulez faire une
classe héritée de la classe Scanner, vous devrez commencer par saisir
: java.util.Scanner.
Terminez l'étape
et voici le résultat :
Vous
voyez que l'héritage est bon et que les méthodes de la classe abstraite sont
mises automatiquement !

Pratique,
n'est-ce pas ?
Et au fait, que veut dire @Override
?
L'annotation
@Override doit être utilisée lorsqu'une méthode
redéfinit la méthode de la super classe.
Celle-ci existe dans la classe mère
mais on redéfini son contenu dans la classe fille.

Cependant,
dans notre exemple, c'est Eclipse qui rajoute automatiquement cette annotation,
mais, si vous redéfinissez une méthode d'une classe mère manuellement, vous
pouvez l'ajouter vous même, tout en sachant que
ceci n'est pas
obligatoire !
Allez, en avant pour le topo.
Ce qu'il faut retenir
Avec les classes abstraites, vous devez vous rappelez
ceci :
- une classe est définie comme abstraite avec le mot clé abstract.
- Une classe abstraite ne peut pas être
instanciée.
- Une classe abstraite n'est pas obligée de contenir de méthode abstraite.
- Si une classe contient une méthode abstraite, cette classe doit alors être
déclarée abstraite.
- Une méthode abstraite n'a pas de corps.
- Les classes abstraites sont à utiliser lorsque qu'une classe mère ne doit
pas être instanciée.
Un petit topo ici... Bon... Un QCM vous
attend.
Maintenant, veuillez aller à la partie suivante
: Les interfaces.
Les interfaces
Voici donc l'un des autres fondements du langage : les
interfaces !
Il ne s'agit pas ici d'interfaces
graphiques...
Mais vous allez apercevoir ce que les programmeurs Java appellent le nec
plus ultra du polymorphisme !
Je suis sûr que vous devez être impatients de
voir ce point... Alors, qu'attendons nous ?
Une petite devinette
L'un des atouts majeurs, pour ne pas dire l'atout
majeur, de la programmation orientée objet est la réutilisabilité de vos
objets.
Il est bien commode de pouvoir utiliser un objet, voire même une
architecture que nous avons développée dans une application
nouvelle.
Admettons que l'architecture que nous avons développée dans les
chapitres précédents forme une bonne base. Que se passerait-il si un autre
développeur vous demande la possibilité d'utiliser vos objets dans un autre type
d'application ?
Dans la nôtre, nous ne nous sommes occupés que de l'aspect
générique des animaux que nous avons créés. Cependant, la personne qui vous a
contacté, lui, développe une application pour un chenil.

La
principale contrainte, c'est que vos chiens vont devoir apprendre à faire de
nouvelles choses comme :
- faire le beau,
- faire des calins,
- faire une léchouille.
Je ne vois pas le problème ! Tu n'as qu'à ajouter ces
méthodes dans la classe Animal.
Oula ! Vous vous rendez bien
compte que vous allez avoir des lions qui vont faire le beau ?
Dans ce cas, on n'a qu'à mettre ces méthodes dans la
classe Chien !
Ceci
pourrait être une solution, mais j'y vois deux contre-indications :
- vous allez devoir mettre en oeuvre une convention de nommage entre vous et
le programmeur qui va utiliser vos objets... Vous ne devrez pas utiliser une
méthode faireCalin() alors que le
programmeur utilise une méthode faireUnCalin() ;
- si vous faites ceci. ADIEU LE
POLYMORPHISME ! Vous ne pourrez pas appeler vos objets par le
biais d'un super type. Pour pouvoir accéder à ces méthodes, vous devrez
obligatoirement passer par une référence à un objet Chien. Pas terrible, tout ça...
Tu nous a dit que pour utiliser au mieux le
polymorphisme, nous devions définir les méthodes au plus haut niveau de la
hiérarchie.
Tout à fait.
Alors du coup, il faut redéfinir un super type pour
pouvoir utiliser le polymorphisme !
Oui, et je vous rappelle que
l'héritage multiple est interdit en Java. Et quand je dis interdit, je veux dire
que Java ne gère pas ça !
Nous sommes sur la bonne voie. Il faudrait pouvoir
développer un nouveau super type et s'en servir dans nos classes
Chien.
Eh bien nous pouvons faire cela avec
des
interfaces.
En fait, les
interfaces permettent de créer un nouveau super type ; on peut même en ajouter
autant que nous le souhaitons dans une seule classe !

En
plus, concernant l'utilisation de nos objets, la convention est toute trouvée...
Pourquoi ? Parce qu'une interface n'est rien d'autre qu'une classe 100 %
abstraite !
Allez : venons-en au fait !

Votre première interface
Pour définir une interface, au lieu d'écrire :
Code : Java
il vous suffit de faire :
Code : Java
Voilà, vous venez
d'apprendre à déclarer une interface. Maintenant, tant qu'à y ajouter des
méthodes, vu qu'une interface est une classe 100 % abstraite, vous n'avez qu'à y
mettre des méthodes abstraites. Mais sans mettre le mot clé
abstract.
Voici des
exemples d'interfaces :
Code : Java
1
2
3
4
5
6 |
public interface I{
public void A();
public String B();
}
|
Code :
Java
1
2
3
4
5
6 |
public interface I2{
public void C();
public String D();
}
|
Et pour faire en sorte
qu'une classe utilise une interface, il suffit d'utiliser le mot clé
implements.
Ce qui nous donnerait
:
Code : Java
1
2
3
4
5
6
7
8
9 |
public class X implements I{
public void A(){
//.......
}
public String B(){
//.......
}
}
|
Voilà, c'est tout.
On
dit que la classe X implémente l'interface I.
Et donc, comme je vous
le disais, vous pouvez implémenter plusieurs interfaces, voilà donc comment ça
se passe :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
public class X implements I, I2{
public void A(){
//.......
}
public String B(){
//.......
}
public void C(){
//.......
}
public String D(){
//.......
}
}
|
Par contre, lorsque vous implémentez une ou plusieurs
interfaces, vous DEVEZ OBLIGATOIREMENT redéfinir leurs méthodes
!
Et ainsi, avec le polymorphisme, vous pouvez faire ceci :
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
public class Test{
public static void main(String[] args){
I var = new X();//Avec cette référence, vous pouvez utiliser de façon polymorphe les méthodes de l'interface I
I2 var2 = new X();//Avec cette référence, vous pouvez utiliser de façon polymorphe les méthodes de l'interface I2
var.A();
var2.C();
}
}
|
Maintenant que vous savez
cela, attelons-nous à notre programme.
Implémentation de l'interface Rintintin
Voilà où nous en sommes
- Nous voulons que nos chiens puisse être amicaux.
- Nous voulons définir un super type pour pouvoir utiliser le polymorphisme.
- Nous voulons pouvoir continuer à utiliser nos objets comme avant.
Comme le titre de cette sous-partie le stipule, nous allons
créer l'interface
Rintintin pour
ensuite l'implémenter dans notre objet
Chien.
Sous Eclipse, vous pouvez faire
File /
New /
Interface, ou simplement cliquer sur la flèche noire à côté
du C pour la création de classe, et choisir
interface.
Voici
son code :
Code : Java
1
2
3
4
5
6
7 |
public interface Rintintin{
public void faireCalin();
public void faireLechouille();
public void faireLeBeau();
}
|
À présent, il ne nous reste
plus qu'à implémenter l'interface dans notre classe
Chien. Ce qui nous donne :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 |
public class Chien extends Canin implements Rintintin {
public Chien(){
}
public Chien(String couleur, int poids){
this.couleur = couleur;
this.poids = poids;
}
void crier() {
System.out.println("J'aboie sans raison ! ");
}
public void faireCalin() {
System.out.println("Je te fais un GROS CÂLIN");
}
public void faireLeBeau() {
System.out.println("Je fais le beau !");
}
public void faireLechouille() {
System.out.println("Je fais de grosses léchouilles...");
}
}
|
L'ordre des déclarations est
PRIMORDIAL. Vous DEVEZ mettre l'expression d'héritage AVANT l'expression
d'implémentation, SINON votre code ne compilera pas
!
Voici un code que vous pouvez utiliser pour
tester le polymorphisme de notre implémentation :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 |
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
//Les méthodes d'un chien
Chien c = new Chien("Gris bleuté", 20);
c.boire();
c.manger();
c.deplacement();
c.crier();
System.out.println(c.toString());
System.out.println("--------------------------------------------");
//Les méthodes de l'interface
c.faireCalin();
c.faireLeBeau();
c.faireLechouille();
System.out.println("--------------------------------------------");
//Utilisons le polymorphisme de notre interface
Rintintin r = new Chien();
r.faireLeBeau();
r.faireCalin();
r.faireLechouille();
}
}
|
Objectif atteint !

Nous
avons réussi à définir deux super classes afin de les utiliser comme super types
et de jouir pleinement du polymorphisme !
Nous allons voir au chapitre
suivant qu'il y a une façon d'utiliser les interfaces très intéressante ! Mais
pour le moment, voyons un peu à quoi ressemble notre diagramme, à présent
:
Nous
voyons bien notre interface
Rintintin ainsi que ses méthodes, et notre flèche
pointillée qui se dirige vers notre interface signifie
"
implémente".
Nous pouvons lire
Chien implemente Rintintin.
Bon : c'est l'heure d'une petie
astuce de notre IDE préféré, il me semble...
Astuce d'Eclipse
Astuce 1
Je vous ai expliqué comment
créer directement une interface.
Mais vous avez sans doute remarqué qu'une
erreur persistante apparaît lorsque vous saisissez
implements Rintintin après la déclaration de
votre classe.
Je pense même que vous devez avoir une zolie croix rouge à
côté du nom de la classe, comme celle-ci :
Cette erreur est dûe au fait que vous
n'avez pas implémenté les méthodes de l'interfaces !
Cliquez
sur cette croix rouge et choisissez l'option
add
unimplemented method : Eclipse ajoutera automatiquement toutes les
méthodes à implémenter par la classe.
Astuce 2
Je vais vous demander de
faire appel à de vieux souvenirs provenant du chapitre précédent. Rappelez-vous
l'astuce d'Eclipse afin de définir la super classe pour l'héritage. Eh bien
Eclipse fait la même chose pour les interfaces...

Si,
si, je vous assure. Regardez plutôt comment on crée une classe
ChienSauvage héritant d'
Animal et implémentant
Rintintin.
On crée une nouvelle classe
héritant d'
Animal (vous savez
faire) et avant de valider, regardez juste en dessous :
Cliquez
à présent sur
Add, vous arrivez sur une fenêtre où il
vous est demandé quelle interface vous voulez implémenter. Commencez à taper
Rintintin et, grâce à
l'autocomplétion, Eclipse vous fait une sélection des interfaces disponibles
dont
Rintintin. Voyez plutôt
:
Il
ne vous reste plus qu'à cliquer sur
OK, et voyez le
résulat :
Votre
classe implémente l'interface
Rintintin.
Terminez l'opération : vous avez
une classe qui hérite d'
Animal ET
qui implémente
Rintintin. Celle-ci
redéfinit toutes les méthodes abstraites de la classe abstraite ainsi que toutes
les méthodes de l'interface.

On
dit merci qui ?
Bon : je crois qu'un topo est indispensable.
Ce qu'il faut retenir
Alors, ici, la nouveauté vient de l'interface... Mais
récapitulons un peu ce que nous avons vu.
- Une interface est une classe 100 % abstraite.
- Toutes les méthodes d'une interface n'ont pas de corps.
- Une interface sert à définir un super type et à utiliser le
polymorphisme.
- Une interface s'implémente dans une classe en utilisant le mot clé
implements.
- Vous pouvez implémenter autant d'interfaces que vous voulez dans vos
classes.
- Vous devez redéfinir toutes les méthodes de
l'interface (ou les interfaces) dans votre classe.
Allez, c'est l'heure du QCM !
Prenez bien le temps de comprendre les deux chapitres
que nous venons de voir.
Ceux-ci forment l'un des gros blocs du langage et
vous verrez, lorsque nous aborderons les interfaces graphiques, que ces deux
concepts sont omniprésents !

Pour
le moment, nous allons continuer de voir les fondements de la programmation
orientée objet, et poursuivons tout de suite par
les
exceptions !
Les exceptions
Voici encore une notion très importante en programmation
Java.
Exception : erreur se produisant dans un programme
conduisant le plus souvent à l'arrêt de celui-ci.
Il vous est
sûrement déjà arrivé d'avoir un gros message affiché en rouge dans la console
d'eclipse : eh bien ceci a été généré par une exception... qui n'a pas été
capturée. La gestion des exceptions
s'appelle aussi la
capture d'exception !
Le
principe consiste à repérer un morceau de code qui pourrait générer une
exception (une division par zéro, par exemple), de capturer l'exception
correspondante, et enfin de gérer celle-ci, c'est-à-dire d'afficher un message
personnalisé et de continuer le traitement.
Bon : vous voyez maintenant
ce que nous allons aborder durant ce chapitre... Donc, allons-y !
Premier exemple d'exception et le bloc try{....}
catch{...}
Pour vous faire comprendre le principe des exceptions,
je dois tout d'abord vous dire que Java contient une classe nommée
Exception, où sont répertoriés différents cas
d'erreurs. La division par zéro dont je vous parlais tout à l'heure en fait
partie !
Toutes les classes de Java possèdent des exceptions
: par exemple, la classe java.io,
qui gère les entrées - sorties, a, entre autres, l'exception IOException.
Je ne peux
malheureusement pas vous en faire toute la liste, mais.... je peux vous donner
une astuce pour savoir de laquelle il s'agit... Et ainsi pouvoir la capturer, et
afficher un message personnalisé, sans que votre programme ne s'arrête.
Créez un nouveau projet avec seulement la classe
main, et mettez-y le code suivant
:
Code : Java
1
2
3 |
int j = 20, i = 0;
System.out.println(j/i);
System.out.println("coucou toi !");
|
Vous devriez avoir un zoli
message d'erreur Java (en rouge) comme celui-ci :
Mais
surtout, vous devez vous rendre compte que lorsque l'exception a été levée,
le programme s'est arrêté !
Dans ce
message, le nom de l'exception qui a été déclenchée est
ArithmeticException. Nous savons donc maintenant
qu'une division par zéro est une
ArithmeticException. Nous allons pouvoir la
capturer, et réaliser un traitement en conséquence.
Une exception se capture grâce à un bloc
try {ici, le
code susceptible de générer une exception} ; le traitement associé, lorsque le bloc try{...} capture une exception, se trouve dans un bloc
catch {ici, ce
que le programme doit faire si une exception est capturée}.
Ce que je vous propose
maintenant, c'est de capturer l'exception de notre division par zéro, et
d'afficher un message personnalisé. Pour ce faire, tapez le code suivant dans
votre
main :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
int j = 20, i = 0;
try {
System.out.println(j/i);
} catch (ArithmeticException e) {
// TODO Auto-generated catch block
System.out.println("Division par zéro !");
}
System.out.println("coucou toi !");
}
}
|
Si vous exécutez ce code,
vous devez avoir ceci sous les yeux :

Je
tiens tout d'abord à vous féliciter : vous venez de capturer votre première
exception en Java !
Voyons un peu ce qui se passe :
- nous initialisons deux variables de type int,
l'une à 0, et l'autre à un chiffre quelconque.
- Ensuite, nous isolons le code susceptible de générer une exception, ici :
System.out.println(j/i);.
- Lorsque le programme atteint cette ligne, une exception de type
ArithmeticException est levée.
- Notre bloc catch contient justement un objet
de type ArithmeticException en
paramètre. Nous l'avons appelé e.
- L'exception étant capturée, l'instruction du bloc catch s'exécute !
- Notre message d'erreur personnalisé s'affiche alors à l'écran.
Lorsque nous capturons une exception, nous pouvons
aussi dire que nous levons une exception...
D'accord, mais le paramètre de notre bloc catch, il sert à quoi, au juste ?
Il sert à
savoir quel type d'exception doit être capturé. Et l'objet -
ici, e- peut
servir à agrémenter notre message, grâce à l'appel de la méthode
getMessage().
Faites à nouveau ce même test,
en remplaçant l'instruction du
catch par celle-ci
:
Code : Java
1 |
System.out.println("Division par zéro !" + e.getMessage());
|
Vous devez maintenant avoir
ceci :
Voilà
ce que vous renvoie la fonction
getMessage().
Je vous disais aussi que le
principe de capture d'exception permettait de ne pas arrêter l'exécution du
programme. Et vous avez pu le constater par vous-mêmes !

Lorsque
nous capturons une exception, le code présent dans le bloc
catch(){...} est
exécuté mais le programme poursuit son cours !
Mais ce que vous ignorez
sûrement, c'est que nous pouvons créer et intercepter nos propres exceptions
!
Si nous passions tout de suite à la prochaine partie de ce chapitre ?
Les exceptions personnalisées
À partir de maintenant, nous allons nous servir à
nouveau de notre projet
Ville (celui que vous avez
utilisé dans les premiers chapitres...).
Nous allons perfectionner un peu la
gestion de nos objets
Ville et
Capitale... Comment ? Eh bien je
vois bien une exception qui pourrait être créée... Et je suis sûr que certains
petits malins se sont déjà amusés à créer des villes ou des capitales avec un
nombres d'habitants négatif....
Je vous propose simplement de mettre en oeuvre une exception de notre
cru, ceci afin de pouvoir interdire l'instanciation d'objet
Ville ou
Capitale ayant un nombre négatif
d'habitants.
La procédure pour faire ce tour de force est un peu
particulière :
- Nous devons créer une classe héritée de la classe Exception : appelons-la NombreHabitantException. Par convention,
les exceptions ont un nom se terminant par Exception.
- Nous devons renvoyer l'exception levée à notre classe NombreHabitantException.
- Ensuite, gérer celle-ci dans notre classe NombreHabitantException.
Comment faire tout ça ?
Eh bien je vais vous
apprendre encore deux autre mots clé !
Non, ne grimacez pas... Je vous assure que si vous en retenez un, vous allez
retenir l'autre...
Le premier mot clé
throwsCe mot clé
permet de dire à une instruction Java (condition, déclaration de variable...) ou
à une classe entière qu'une exception potentielle sera gérée par une classe
-
souvent une classe personnalisée- mais ce peut être
la classe
Exception elle-même. Ce
mot clé est suivi du nom de la classe qui va gérer l'exception. Ceci a pour but
de définir le type d'exception qui risque d'être générée par l'instruction, ou
la classe qui précède le mot clé
throws.
Le deuxième mot clé
throwCelui-ci
permet d'instancier un objet dans la classe suivant l'instruction
throws. Cette instruction est suivie du mot clé
new ainsi que d'un objet
cité avec
throws. En fait, il lance
une exception, tout simplement.
Faites surtout bien attention à ne pas confondre ces
deux mots clé.
Pour pouvoir mettre en pratique ce système, nous
devons commencer par créer une classe qui va gérer nos exceptions. Celle-ci, je
vous le rappelle, doit être héritée d'
Exception. Pour commencer, inutile de créer un
constructeur, ce qui nous donnerait une classe
Erreur, héritée de
Exception,
vide.
Comme ceci :
Code : Java
1
2
3
4
5
6
7 |
class NombreHabitantException extends Exception{
public NombreHabitantException(){
System.out.println("Vous essayez d'instancier une classe Ville avec un nombre d'habitants négatif !");
}
}
|
Reprenez votre projet avec vos
classes
Ville,
Capitale et créez maintenant une classe
NombreHabitantException, comme je
viens de le faire !
Maintenant, c'est dans le constructeur de nos objets
que nous allons mettre une condition qui, si elle est remplie, lève une
exception de type
Erreur.
En gros, nous devons dire à notre constructeur de ville :
"
Si l'utilisateur créer une instance ville avec une nombre
d'habitants négatif, créer un objet de type Erreur (hérité d'Exception).
Le constructeur
d'initialisation de ville doit ressembler à ce qui suit,
maintenant.Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
public Ville(String pNom, int pNbre, String pPays) throws NombreHabitantException
{
if(pNbre < 0)
throw new NombreHabitantException();
else
{
nbreInstance++;
nbreInstanceBis++;
nomVille = pNom;
nomPays = pPays;
nbreHabitant = pNbre;
this.setCategorie();
}
}
|
throws NombreHabitantException nous indique que si
une erreur est capturée, celle-ci sera traitée en tant qu'objet de la classe
NombreHabitantException ! Ce qui, au final, nous
renseigne sur le type de l'erreur en question.
throw new NombreHabitantException(); instancie la
classe
Erreur si la condition
if(nbre < 0) est
remplie.
Maintenant que vous avez fait cette petite modification,
retournez dans votre classe
main,
effacez son contenu, puis créez un objet
ville de
votre choix !
Et à présent, vous devez voir une erreur persistante ; c'est
tout à fait normal, ce qui doit vous donner ceci :
Ceci
signifie qu'à partir de maintenant, dû aux changements dans le constructeur,
vous devrez gérer les exceptions possibles sur cette instruction. Avec un bloc
try{} catch{}.
On dit aussi que votre constructeur est devenu une
méthode à risque, et vous avez laissé le soin au développeur de
gérer l'exception potentielle !
Donc, pour que l'erreur
disparaisse, il nous faut entourer notre instanciation avec un bloc
try{...}catch{...}.
Comme ceci :
Vous
pouvez constater que l'erreur a disparu et que notre code compile et s'exécute
correctement.

Par
contre, il faut que vous soyez préparés à une chose. Le code que j'ai utilisé
ci-dessus fonctionne très bien, mais ce code :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12 |
public class Sdz1 {
public static void main(String[] args)
{
try {
Ville v = new Ville("Rennes", 12000, "France");
} catch (NombreHabitantException e) {}
System.out.println(v.toString());
}
}
|
ne fonctionnera pas et pour
appuyer mes dires, voici le témoignage de quelqu'un d'intègre :
Vous
pouvez constater qu'Eclispe n'aime pas du tout notre code !
Pourquoi cela ?
Tout simplement car la
déclaration de votre objet
Ville
est faite dans un sous-bloc d'instructions, celui du bloc
try{}. Et rappelez-vous
:
Une variable déclarée dans un bloc
d'instructions n'existe que dans ce bloc d'instructions
!
Donc, ici, notre objet
v, n'existe pas en dehors de l'instruction
try{}.
Pour
palier à ce problème, il nous
suffit de
déclarer notre objet
en dehors du bloc try{} et de l'instancier à l'intérieur !
Nous
pouvons faire ceci :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13 |
public class Sdz1 {
public static void main(String[] args)
{
Ville v = null;
try {
v = new Ville("Rennes", 12000, "France");
} catch (NombreHabitantException e) {}
System.out.println(v.toString());
}
}
|
Et ce code nous donne :
Code : Console
|
Rennes est une ville de France, elle comporte : 12000 => elle est donc de catégorie : C |
Mais
si nous déclarons une Ville avec un nombre d'habitants négatif pour tester notre
exception ?
Avec ce code, par exemple :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13 |
public class Sdz1 {
public static void main(String[] args)
{
Ville v = null;
try {
v = new Ville("Rennes", 12000, "France");
} catch (NombreHabitantException e) {}
System.out.println(v.toString());
}
}
|
Voici ce que nous obtenons
:
Voyons
ce qu'il s'est passé.
- Nous avons bien déclaré notre objet en dehors du bloc d'instructions.
- Au moment d'instancier celui-ci, une exception est levée ! L'instanciation
échoue lamentablement !
- La clause catch{} est exécutée et notre objet
NombreHabitantException est
instancié. Écriture du message.
- Et lorsque nous arrivons sur l'instruction "System.out.println(v.toString());", notre objet
est null !
- Une NullPointerException est levée !
Ce qui signifie que si notre instanciation a échoué dans
notre bloc
try{}, le programme plantera !
Comment empêcher cela, alors ?
Vous allez
voir, c'est très simple. Il suffit d'instancier un objet
Ville par défaut dans notre bloc
catch{}. Grâce à cela,
si notre instanciation avec valeur échoue, on fait une instanciation par défaut
qui, elle, n'est pas une méthode à risque !
Voyez plutôt :
Vous
pouvez voir que l'exception est bien levée et que notre objet est instancié
mais, surtout, que notre programme ne plante plus !

Maintenant
que nous avons vu la création d'exception, il serait de bon ton que nous
puissions avoir de plus amples renseignements concernant celle-ci.

Par
exemple, il serait peut-être bon de réafficher le nombre d'habitants que l'objet
a reçu...
Pour faire ceci, nous n'avons qu'à créer un deuxième constructeur
dans notre classe
NombreHabitantException, qui prend un nombre
d'habitants en paramètre. Un peu comme ça :
Code :
Java
1
2
3
4
5 |
public NombreHabitantException(int nbre)
{
System.out.println("Instanciation avec une nombre d'habitants négatif");
System.out.println("\t => " + nbre);
}
|
Il vous suffit maintenant de
définir cette construction de notre objet hérité d'
Exception dans votre classe
Ville. Comme ça
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
public Ville(String pNom, int pNbre, String pPays) throws NombreHabitantException
{
if(pNbre < 0)
throw new NombreHabitantException(pNbre); // on appelle le nouveau constructeur
else
{
nbreInstance++;
nbreInstanceBis++;
nomVille = pNom;
nomPays = pPays;
nbreHabitant = pNbre;
this.setCategorie();
}
}
|
Et si vous exécutez le même
code que précédemment, vous obtiendrez ceci :
C'est
pas mal, avouez-le !

Mais
vous devez savoir que l'objet passé en paramètre de la clause
catch a des méthodes
héritées de la classe
Exception. Regardez :
Vous
pouvez les utiliser si vous le voulez et surtout, si vous en avez
l'utilité...
Nous utiliserons certaines de ces méthodes dans les prochains
chapitres...
Ici, la méthode printStackTrace() permet de voir ou se
situe l'exception dans notre code ! Elle vous informe sur le nom de la classe
levant l'exception et le numéro de ligne ou se trouve le code l'ayant
levée.
Je vais vous faire peur : ici, nous avons capturé une
exception mais nous pouvons en capturer plusieurs...
La gestion de plusieurs exceptions
Bien entendu, ceci est valable pour toutes sortes
d'exceptions, qu'elles soient personnalisées, ou faisant partie de Java
!
Disons que nous voulons lever une exception, si le nom de la ville fait
moins de 3 caractères.
Nous allons répéter les premières étapes vues
précédemment, c'est-à-dire créer une classe
NomVilleException:
Code :
Java
1
2
3
4
5
6
7 |
public class NomVilleException extends Exception {
public NomVilleException(String message){
super(message);
}
}
|
Vous avez remarqué que nous
avons utilisé
super. Avec cette redéfinition, nous
pourrons afficher notre message d'erreur en utilisant la méthode
getMessage().
Vous allez
voir.
Maintenant ajoutez une condition dans notre constructeur
Ville :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 |
public Ville(String pNom, int pNbre, String pPays) throws NombreHabitantException, NomVilleException
{
if(pNbre < 0)
throw new NombreHabitantException(pNbre);
if(pNom.length() < 3)
throw new NomVilleException("le nom de la ville est inférieur à 3 caractères ! nom = " + pNom);
else
{
nbreInstance++;
nbreInstanceBis++;
nomVille = pNom;
nomPays = pPays;
nbreHabitant = pNbre;
this.setCategorie();
}
}
|
Vous remarquez que les
différentes erreurs dans l'instruction
throws sont séparées par une virgule
!
Maintenant, nous sommes parés pour la capture de deux exceptions
personnalisées. Regardez comment on gère deux exceptions sur une instruction
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
Ville v = null;
try {
v = new Ville("Re", 12000, "France");
}
//Gestion de l'exception sur le nombre d'habitants
catch (NombreHabitantException e) {
e.printStackTrace();
v = new Ville();
}
//Gestion de l'exception sur le nom de la ville
catch(NomVilleException e2){
System.out.println(e2.getMessage());
v = new Ville();
}
System.out.println(v.toString());
|
Vous pouvez voir comment utiliser la méthode
getMessage() à présent. C'est tout
bête, avouez-le !
Vous pouvez constater qu'un deuxième bloc
catch{} s'est glissé... Eh bien
c'est comme ceci que nous gérerons plusieurs exceptions !
Vous avez aussi
remarqué que j'ai aussi changé le code afin que l'exception sur le nom soit
levée et non plus l'exception sur le nombre d'habitants...
Vous aurez
ceci :
Si
vous mettez un nom de ville de moins de 3 caractères, et un nombre d'habitants
négatif, c'est l'exception du nombre d'habitants qui sera levée en premier ! Et
pour cause... il s'agit de notre première condition dans notre
constructeur...
Lorsque plusieurs exceptions sont gérées par une
portion de code, pensez bien à mettre les blocs catch du plus pertinent au moins pertinent. En fait, il
s'agit des erreurs capturées à mettre par ordre de pertinence. Dans notre
premier exemple d'exception, sur la division par zéro, si nous avions mis un
bloc catch(Exception ex){} avant le
bloc catch(ArithmeticException
e){}, une erreur se serait produite à la compilation, car
Exception est plus générique que
ArithmeticException.
Il y a
une instruction dont je ne vous ai pas encore parlé... Il s'agit de la clause
finally.
Celle-ci est une clause se positionnant après les clauses
catch.
En fait, ce
qui se trouve dans cette clause sera
TOUJOURS exécuté.
Qu'une exception soit levée ou non.
Exemple
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 |
public class Sdz1 {
public static void main(String[] args)
{
Ville v = null;
try {
v = new Ville("Re", 100, "France");
}
//Gestion de l'exception sur le nombre d'habitants
catch (NombreHabitantException e) {
e.printStackTrace();
v = new Ville();
}
//Gestion de l'exception sur le nom de la ville
catch(NomVilleException e2){
System.out.println(e2.getMessage());
v = new Ville();
}
//La fameuse clause finally
finally{
System.out.println("\n---------------------------------------------");
System.out.println("Voici le code qui est toujours exécuté ! ! ! ");
System.out.println("---------------------------------------------\n");
}
System.out.println(v.toString());
}
}
|
Quoi qu'il se passe dans la
classe
try ou
catch, les instructions de la clause
finally
seront TOUJOURS exécutées ! Et vous pouvez faire autant de tests que vous le
voulez.
Euh... d'accord. Mais à quoi ça peut bien nous servir
? On ne peut pas mettre l'instanciation de notre objet ici !
Très
juste !

Par
contre, vous allez apprendre dans les chapitres suivants à ouvrir des flux de
données. Ce genre de code regorge d'exceptions en tout genre et vous serez ravis
de pouvoir fermer votre flux, quoiqu'il arrive !

Bon
: je crois qu'un récapitulatif s'impose mais avant ceci, voici notre rubrique
"
Astuce d'Eclipse".
Astuce d'Eclipse
Il y a plusieurs manières de procéder, mais toutes font
la même chose.
L'astuce ici réside dans le fait de générer les blocs
try{}
catch{} automatiquement. Bien sûr, il faut que les
clauses de déclenchement soient définies au préalable !
Si vous reprenez
le code de votre méthode
main, si
vous effacez le contenu et ajoutez une instanciation de l'objet
Ville sans les clauses
try{} catch{}, vous avez l'erreur
persistante dont je vous parlais au début du chapitre.
Si vous cliquez
sur la croix rouge, située à gauche de votre zone de saisie, vous obtenez ceci
:
Choisissez
l'option
Surround with try/catch et vous avez votre
code, tout beau tout propre !

La
deuxième méthode consiste à sélectionner votre (ou vos) ligne(s) de code à
risque et de faire :
Source / Surround with / try/catch
block ou d'utiliser le raccourci clavier
Control + Alt + Z :
Voici l'image en
utilisant le menu :
Voici
l'image en utilisant le raccourci clavier :
Voilà,
maintenant, rendez-vous au topo habituel !
Ce qu'il faut retenir
- La super classe qui gère les exceptions s'appelle : Exception.
- Lorsqu'un événement que la JVM ne sait pas gérer apparaît, une exception
est levée (ex : division par zéro) !
- Vous pouvez créer une classe d'exception personnalisée en créant une
classe héritant de la classe Exception.
- L'instruction qui permet de capturer des exceptions est le bloc
try{}
catch{}.
- Si une exception est levée dans le bloc try, les instructions figurant dans le
bloc catch seront exécutées si celui-ci
capture l'exception levée !
- Vous pouvez ajouter autant de blocs catch que vous le voulez à la suite d'un
bloc try. Mais respectez l'ordre de
pertinence. Du plus pertinent au moins pertinent !
- Dans une classe objet, vous pouvez prévenir la JVM qu'une méthode est dite
"à risque", ceci grâce au mot clé
throws.
- Vous pouvez définir plusieurs risques d'exceptions sur une même méthode.
Il suffit de séparer les déclarations par une virgule !
- Dans cette méthode, vous pouvez définir les conditions d'instanciation
d'une exception et lancer cette dernière grâce au mot clé throw suivie de
l'instanciation.
- Une instanciation lancée par le biais de l'instruction throw DOIT ÊTRE DÉCLARÉE avec throws au préalable !
!
Je me doute que vous avez dû avoir pas mal de sueurs
froides à la lecture de ce chapitre...
Vous devez savoir aussi que vous
pouvez imbriquer des blocs de captures d'exceptions comme ceci :
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
try{
// Le code sensible pouvant lever une Exception1
try{
//Code sensible pouvant lever une Exception2
}catch(Exception2 e2){}
}
catch(Exception1 e){
}
|
Je vous propose maintenant
de voir
les collections
!
Faites tout de même une pause pour bien comprendre le fonctionnement
des exceptions ; pour les avides de connaissances, rendez-vous au chapitre
suivant !
Les collections d'objets
Voici une partie qui va sans doute plaire...
Ici nous
allons voir qu'il est possible de stocker des données autrement qu'avec des
tableaux !
Et je dirais même plus, ces fameux objets collections sont
dynamiques... en gros, ils n'ont pas de taille à pré-définir. On ne peut donc
pas dépasser leur capacité !
Je
sens que vous êtes impatients. Allons-y, alors !
L'objet LinkedList
Une liste chaînée est une liste dont chaque élément est
relié au suivant par une référence à ce dernier, sa taille n'est pas fixe : on
peut ajouter et enlever des éléments selon nos besoins.
Les
LinkedList acceptent tout type d'objet.
Chaque élément
contient une référence sur l'élément suivant sauf pour le dernier : son suivant
est en fait
null.
Cette classe se trouve dans le
package
java.util.
Voici un petit code pour
appuyer mes dires :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 |
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
public class Test {
public static void main(String[] args) {
List l = new LinkedList();
l.add(12);
l.add("toto ! !");
l.add(12.20f);
for(int i = 0; i < l.size(); i++)
System.out.println("Élément à l'index " + i + " = " + l.get(i));
}
}
|
Vous pourrez constater que
tous les éléments s'affichent !
Maintenant, vous devez savoir autre chose sur
ce genre d'objet. Ceux-ci implémentent l'interface
Iterator. Ceci signifie
que nous pouvons utiliser cette interface pour lister notre
LinkedList.
Dans le
code suivant, j'ai rajouté le parcours avec un itérateur :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 |
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
public class Test {
public static void main(String[] args) {
List l = new LinkedList();
l.add(12);
l.add("toto ! !");
l.add(12.20f);
for(int i = 0; i < l.size(); i++)
System.out.println("Élément à l'index " + i + " = " + l.get(i));
System.out.println("\n \tParcours avec un itérateur ");
System.out.println("-----------------------------------");
ListIterator li = l.listIterator();
while(li.hasNext())
System.out.println(li.next());
}
}
|
Vous pouvez constater que
les deux manières de procéder sont analogues !
Cependant, je dois vous dire
quelques choses sur les listes chaînées. Vu que les éléments ont une référence à
leur élément suivant, ce type de listes peut être particulièrement lourd
lorsqu'elles deviennent volumineuses !
À utiliser avec précaution. Si vous voulez en savoir plus,
c'est
par là.
Nous allons voir un autre objet de la même famille : les
ArrayList.
L'objet ArrayList
Voici un objet bien pratique.
Un
ArrayList est donc un
de ces objets qui n'ont pas de taille limite, et en plus, ils acceptent
n'importe quel type de données !
null y compris !
Dans un
ArrayList, nous pouvons mettre tout ce que
nous voulons. Vous devez par contre importer la classe
ArrayList. Elle se
trouve dans le package
java.util. Mais vous connaissez une bonne
technique pour importer vos classes !
Pour preuve, voici un morceau de
code qui le prouve :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add(12);
al.add("Une chaîne de caractères !");
al.add(12.20f);
al.add('d');
for(int i = 0; i < al.size(); i++)
{
System.out.println("donnée à l'indice " + i + " = " + al.get(i));
}
}
}
|
Si vous exécutez ce code,
vous aurez :
Je
pense que vous voyez déjà les avantages des
ArrayList.
Vous devez savoir aussi qu'il
existe tout un panel de méthodes dans cet objet :
- add() : permet d'ajouter un
élément.
- get(int index) : retourne
l'élément à l'index demandé.
- remove(int index) : efface
l'entrée à l'indice demandé.
- isEmpty() : renvoie "vrai" si
l'objet est vide.
- removeAll(): efface tout le
contenu de l'objet.
- contains(Object element):
retourne "vrai" si l'élément passé en paramètre est dans l'objet.
- ...
Si vous voulez en savoir plus,
c'est
par là.
Un autre objet pratique, les
Hashtable.
L'objet Hashtable
Table de hachage, si vous
traduisez mot à mot...
Ce type d'objet rentre dans la catégorie des
Map.
Entendez par là qu'ils rangent leur éléments avec un système "
clé-valeur". La clé est unique, mais la valeur,
elle, peut être associée à plusieurs clés !
On parcourt ces objets grâce
aux clés qu'ils contiennent. Vu que notre mémoire peut être défaillante, il
existe un moyen de parcours très simple. En utilisant la classe
Enumeration. L'objet
obtient tout le contenu de notre
Hashtable et permet de le parcourir très
simplement. Regardez, le code suivant insère les saisons avec des clés qui ne se
suivent pas, et notre énumération récupère seulement les valeurs :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
import java.util.Enumeration;
import java.util.Hashtable;
public class Test {
public static void main(String[] args) {
Hashtable ht = new Hashtable();
ht.put(1, "printemps");
ht.put(10, "été");
ht.put(12, "automne");
ht.put(45, "hiver");
Enumeration e = ht.elements();
while(e.hasMoreElements())
System.out.println(e.nextElement());
}
}
|
Vous constaterez que le code
marche très bien et qu'il est très simple d'utilisation.
Cet objet nous offre
lui aussi tout un panel de méthodes utiles :
- isEmpty() : retourne "vrai"
si l'objet est vide.
- contains(Object value) :
retourne "vrai" si la valeur est présente. Identique à containsValue(Object value).
- containsKey(int key) :
retourne "vrai" si la clé passée en paramètre est présente.
- put(int key, Object value) :
ajoute le couple key/value dans l'objet.
- elements() : retoune une
énumération des éléments de l'objet.
- keys() : retourne la liste
des clés sous forme d'énumération.
- ...
Vous devez savoir qu'il existe un autre objet de type
Map : il
s'agit du
HashMap ; son utilisation ne diffère que
très peu du
Hashtable. Je n'en parlerai donc pas...
Si vous voulez en savoir plus sur les
Hashtable,
c'est
par là.
L'objet HashSet
Un
Set est une collection
qui n'accepte pas les doublons. Elle n'accepte qu'une seule fois la valeur
null, car
deux fois cette valeur est considérée comme un doublon.
On peut dire que cet
objet n'a que des éléments différents en son sein !
Certains
Set sont plus restrictifs que d'autres, n'acceptent pas
null ou un certain type d'objet.
On peut
parcourir ce type de collection avec un objet
Iterator ou, cet objet peut retourner un
tableau d'
Object.
Voici un code simple
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 |
import java.util.HashSet;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add("toto");
hs.add(12);
hs.add('d');
Iterator it = hs.iterator();
while(it.hasNext())
System.out.println(it.next());
System.out.println("\nParcours avec un tableau d'objet");
System.out.println("-----------------------------------");
Object[] obj = hs.toArray();
for(Object o : obj)
System.out.println(o);
}
}
|
Voici une liste des méthodes
que l'on peut trouver dans cet objet :
- Add() : ajoute un élément.
- contains(Object value) :
retourne "vrai" si l'objet contient value.
- isEmpty() : retourne "vrai"
si l'objet est vide.
- iterator(): renvoie un objet
de type Iterator.
- remove(Object o) : retire
l'objet o de la collection.
- toArray() : retourne un
tableau d'Object.
- ...
Pour en savoir plus sur les HashSet,
c'est par
là.
Voilà : nous avons vu quelque chose d'assez intéressant. Je ne
pense pas qu'un QCM soit de mise pour cette partie...

Profitez-en !
Allez ! Zou...
Voici encore un chapitre important !
Surtout prenez
bien le temps de faire des tests, de voir comment se comporte chaque
objet...
Le principal problème qui va se poser maintenant, c'est :
Quelle collection utiliser ?
Pour ceci, je ne
peux pas vous aider... cela dépendra du type de besoin que vous aurez...
Personnellement, je me sers le plus souvent d'
ArrayList et de
Hashtable.
Dès que vous vous sentez
prêts, en avant pour la
généricité en
Java.
La généricité en Java
Pour ce concept, ajouté au JDK depuis sa version 1.5, nous
allons surtout travailler avec des exemples tout au long de ce chapitre.
Le
principe de la généricité est de faire des classes qui n'acceptent qu'un certain
type d'objet ou de donnée, mais de façon dynamique ! :D.
Avec ce que nous
avons vu au chapitre précédent, vous avez sûrement poussé un soupir de
soulagement lorsque vous avez vu que ces objets acceptent tous types de données
!
Par contre, un problème de taille se pose : lorsque vous voudrez
travailler avec ces données, vous allez devoir faire un
cast ! Et peut-être même un
cast de
cast, voire même un
cast de
cast de
cast...
C'est là que se situe le
problème... Mais comme je vous le disais, depuis la version 1.5 du JDK, la
généricité est là pour vous aider !
Notion de base
Bon, pour vous montrer la puissance de la généricité,
nous allons tout de suite voir un exemple de classe qui ne l'utilise pas !
Ne vous en faites pas... Ayez confiance en moi...
Il y a un exemple
très simple, que vous pourrez retrouver aisément sur le net, car il s'agit d'un
des cas les plus simples pour expliquer les bases de la généricité. Nous allons
coder une classe
Solo. Celle-ci va
travailler avec des références de type
String.
Voici le diagramme de classe
:
Vous
pouvez voir que le code de cette classe est très rudimentaire ! On affecte une
valeur, on peut la mettre à jour et la récupérer...
Maintenant, si je vous demande de me faire une classe qui permette de
travailler avec n'importe quel type de données, j'ai une vague idée de ce que
vous allez faire... Ce ne serait pas un truc comme ça :
J'en
étais sûr... :D. Créez-la et créez-vous aussi une classe avec une méthode
main !
Mais si vous voulez
utiliser les données de l'objet
Solo, vous allez devoir faire un
cast.
Testez ce code dans votre
main :
Code : Java
1
2
3
4
5
6
7
8
9 |
public class Test {
public static void main(String[] args) {
Solo val = new Solo(12);
int nbre = val.getValeur();
}
}
|
Vous constatez que vous
tentez vainement de mettre un objet de type
Object dans un objet de type
Integer. Ceci est
interdit ! !
La classe
Object est plus globale que la classe
Integer,
vous ne pouvez donc pas faire cette manipulation, sauf si vous "castez" votre
objet en
Integer, comme ceci :
Code : Java
1
2
3
4
5
6
7
8
9 |
public class Test {
public static void main(String[] args) {
Solo val = new Solo(12);
int nbre = (Integer)val.getValeur();
}
}
|
Pour le moment, on peut dire
que votre classe peut travailler avec tous les types de données, mais les choses
se corsent un peu à l'utilisation... Vous serez peut-être tentés de faire une
classe par type de donnée (
SoloInt,
SoloString).
Et c'est là que la généricité est
pratique. Car avec ceci, vous allez pouvoir savoir ce que contient votre objet
Solo, et vous n'aurez qu'une seule
classe à développer !
Voilà le diagramme de classe de cet objet :
Et
voici son code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 |
public class Solo<T> {
/**
* Variable d'instance
*/
private T valeur;
/**
* costructeur par défaut
*/
public Solo(){
this.valeur = null;
}
/**
* Constructeur avec paramètre
* Inconnu pour l'instant
* @param val
*/
public Solo(T val){
this.valeur = val;
}
/**
* Définit la valeur avec le paramètre
* @param val
*/
public void setValeur(T val){
this.valeur = val;
}
/**
* retourne la valeur déjà "castée" par la signature de la méthode !
* @return
*/
public T getValeur(){
return this.valeur;
}
}
|
Impressionnant, n'est-ce pas
?
Dans cette classe, le
T n'est
pas encore défini. Vous le ferez à l'instanciation de cette classe. Par contre,
une fois instancié avec un type, l'objet ne pourra travailler qu'avec le type de
données que vous lui avez spécifié ! Exemple de code :
Code : Java
1
2
3
4
5
6
7
8 |
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Solo<Integer> val = new Solo<Integer>(12);
int nbre = val.getValeur();
}
}
|
Ce code fonctionne très bien,
mais si vous essayez de faire ceci :
Code : Java
1
2
3
4
5
6
7
8 |
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Solo<Integer> val = new Solo<Integer>("toto"); //Ici on essaie de mettre une chaîne de caractère à la place d'un entier
int nbre = val.getValeur();
}
}
|
...ou encore ceci :
Code : Java
1
2
3
4
5
6
7
8 |
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Solo<Integer> val = new Solo<Integer>(12);
val.setValeur(12.2f); //Ici on essaie de mettre un float à la place d'un entier
}
}
|
...vous verrez une erreur
dans votre zone de saisie. Ceci vous indique que votre objet ne reçoit pas le
bon type d'argument, ou que votre réceptacle n'a pas le bon type de données !
Dans tous les cas de figure, il y a conflit entre le type de données que vous
avez passé à votre instance lors de sa création et le type de données que vous
essayez d'utiliser avec celle-ci !
Par contre, vous devez savoir que cette classe ne
fonctionne pas seulement avec des Integer. Vous pouvez utiliser tous les types que
vous souhaitez !
Voici une démonstration de mes dires :
Code : Java
1
2
3
4
5
6
7
8
9
10 |
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Solo<Integer> val = new Solo<Integer>();
Solo<String> valS = new Solo<String>("TOTOTOTO");
Solo<Float> valF = new Solo<Float>(12.2f);
Solo<Double> valD = new Solo<Double>(12.202568);
}
}
|
Vous devez avoir remarqué que je n'ai pas utilisé
ici les types de données que vous utilisez pour déclarer des variables de type
primitif ! Ce sont les classes de ces types primitifs !
En effet,
lorsque vous déclarez une variable de type primitif, vous pouvez utiliser leurs
classes de définition, mais c'est rarement utilisé car très lourd à la lecture.
Par exemple :
Code : Java
1
2
3
4
5
6
7
8
9 |
public class Test{
public static void main(String[] args){
int i = new Integer(12); // est équivalent à int i = 12;
double d = new Double(12.2586); // est équivalent à double d = 12.2586;
//...
}
}
|
Bon ! Maintenant que vous
avez un bel exemple de généricité, nous allons complexifier un peu les choses !
Plus loin dans la généricité !
Vous devez savoir que la généricité peut être multiple
!
Nous avons créé une classe
Solo, mais rien ne vous empêche de créer une
classe
Duo, qui elle, prend deux
paramètres génériques ! Voici la modélisation de cette classe :
Vous
pouvez voir que cette classe prend deux types de références non encore définies
!
Voilà le code source de cette classe :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 |
public class Duo<T, S> {
/**
* Variable d'instance de type T
*/
private T valeur1;
/**
* Variable d'instance de type S
*/
private S valeur2;
/**
* Constructeur par défaut
*/
public Duo(){
this.valeur1 = null;
this.valeur2 = null;
}
/**
* Constructeur avec paramètres
* @param val1
* @param val2
*/
public Duo(T val1, S val2){
this.valeur1 = val1;
this.valeur2 = val2;
}
/**
* Méthodes d'initialisation des deux valeurs
* @param val1
* @param val2
*/
public void setValeur(T val1, S val2){
this.valeur1 = val1;
this.valeur2 = val2;
}
/**
* Retourne la valeur T
* @return
*/
public T getValeur1() {
return valeur1;
}
/**
* Définit la valeur T
* @param valeur1
*/
public void setValeur1(T valeur1) {
this.valeur1 = valeur1;
}
/**
* retourne la valeur S
* @return
*/
public S getValeur2() {
return valeur2;
}
/**
* définit la valeur S
* @param valeur2
*/
public void setValeur2(S valeur2) {
this.valeur2 = valeur2;
}
}
|
Voici un code que vous
pouvez tester :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12 |
public class Test {
public static void main(String[] args) {
Duo<String, Boolean> dual = new Duo<String, Boolean>("toto", true);
System.out.println("Valeur de l'objet dual: val1 = " + dual.getValeur1() + ", val2 = " + dual.getValeur2());
Duo<Double, Character> dual2 = new Duo<Double, Character>(12.25895, 'C');
System.out.println("Valeur de l'objet dual2: val1 = " + dual2.getValeur1() + ", val2 = " + dual2.getValeur2());
}
}
|
Et voici le résultat :
Vous
voyez qu'il n'y a rien de bien méchant ici. Ce principe fonctionne exactement
comme l'exemple précédent. La seule différence est dans le fait qu'il n'y a pas
un, mais deux paramètres génériques !
Attends une minute... Lorsque je déclare une référence
de type Duo<String, Boolean>,
je ne peux plus la changer en un autre type !
En fait, avec ce que je
vous ai fait voir, non.
Pour le moment, si vous faites :
Code : Java
1
2
3
4
5
6
7
8
9 |
public class Test {
public static void main(String[] args) {
Duo<String, Boolean> dual = new Duo<String, Boolean>("toto", true);
System.out.println("Valeur de l'objet dual: val1 = " + dual.getValeur1() + ", val2 = " + dual.getValeur2());
dual = new Duo<Double, Character>();
}
}
|
vous violez la contrainte que
vous avez émise lors de la déclaration du type de référence ! Mais il existe un
moyen de contourner ça.

Tout
simplement en disant, à la déclaration, que votre objet va accepter tout types
de références ! Comment en utilisant ce qu'on appelle le
wildcard :
?.
Comme ceci :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12 |
public class Test {
public static void main(String[] args) {
Duo<?, ?> dual = new Duo<String, Boolean>("toto", true);
System.out.println("Valeur de l'objet dual: val1 = " + dual.getValeur1() + ", val2 = " + dual.getValeur2());
dual = new Duo<Double, Character>();
dual = new Duo<Integer, Float>();
dual = new Duo<Solo, Solo>();
}
}
|
Avec ce type de déclaration,
votre objet accepte bien n'importe quel type de référence !
Intéressant, non
?
Donc si vous suivez bien, on va pouvoir encore corser la chose !
Généricité et collection
Vous pouvez aussi utiliser la généricité sur les objets
servant à gérer des collections.
C'est même l'un des points les plus utiles
de la généricité !

En
effet, lorsque vous listiez le contenu d'un
ArrayList par exemple, vous n'étiez JAMAIS
sûrs à 100 % de savoir sur quel type de référence vous alliez tomber... Eh bien
ce calvaire est terminé et le polymorphisme va pouvoir réapparaître, plus
puissant que jamais !
Voyez comment utiliser (même si vous l'aviez
deviné) la généricité avec les collections :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 |
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
System.out.println("Liste de String");
System.out.println("------------------------------");
ArrayList<String> listeString= new ArrayList<String>();
listeString.add("Une chaîne");
listeString.add("Une Autre");
listeString.add("Encore une autre");
listeString.add("Allez, une dernière");
for(String str : listeString)
System.out.println(str);
System.out.println("\nListe de float");
System.out.println("------------------------------");
ArrayList<Float> listeFloat = new ArrayList<Float>();
listeFloat.add(12.25f);
listeFloat.add(15.25f);
listeFloat.add(2.25f);
listeFloat.add(128764.25f);
for(float f : listeFloat)
System.out.println(f);
}
}
|
Voici le résultat de ce code
:
La généricité sur les listes est régi par les
même lois vues précédemment !
Pas de type float dans un
ArrayList<String>.
Vu
qu'on y a
crescendo, on pimente à nouveau le tout !
Héritage et généricité
Là où les choses sont pernicieuses, c'est quand vous
utilisez des classes usant de la généricité avec des objets usant de la notion
d'héritage !
L'héritage dans la généricité est une des choses les plus
complexes à comprendre en Java. Pourquoi ? Tout simplement parce qu'elle va à
l'encontre de ce que vous avez appris jusqu'à présent...
Acceptons le postulat suivant
Nous avons
une classe
Voiture dont hérite une
autre classe
VoitureSansPermis, ce
qui nous donnerait le diagramme suivant :
Jusque-là,
c'est simplissime.

Maintenant,
ça se complique :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13 |
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Voiture> listVoiture = new ArrayList<Voiture>();
ArrayList<VoitureSansPermis> listVoitureSP = new ArrayList<VoitureSansPermis>();
listVoiture = listVoitureSP;//Interdit ! ! ! !
}
}
|
Je sais que même si vous aviez
l'habitude de la covariance des variables, ceci n'existe pas sous cette forme
avec la généricité !
Pourquoi cela ?
Imaginez deux secondes que
l'instruction interdite soit permise !
Dans
listVoiture, vous avez le contenu de la liste des
voitures sans permis, et rien ne vous empêche d'ajouter une voiture... Là où le
problème prend toute son envergure, c'est lorsque vous allez vouloir sortir
toutes les voitures sans permis de votre variable
listVoiture, eh oui ! Vous y avez rajouté une
voiture !
Lors du balayage de la liste vous aurez, à un moment, une référence de type
VoitureSansPermis à qui vous tentez
d'affecter une référence de type
Voiture. Voilà pourquoi ceci est INTERDIT !
!
L'une des solutions consiste à utiliser le
wildcard.
Je vais maintenant vous indiquer
quelque chose d'important !
Avec la généricité, vous pouvez aller encore plus
loin... Nous avons vu comment restreindre le contenu d'une de nos listes. Mais
nous pouvons aussi élargir son contenu ! Si je veux par exemple qu'un
ArrayList
puisse avoir toutes les instances de
Voiture et de ses classes filles.
Comment
faire ?
Ce qui suit s'applique aussi aux interfaces
susceptibles d'être implémentées par une classe
!
Attention les yeux, ça pique :
Code
: Java
1
2
3
4
5
6
7
8 |
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
//Voici un ArrayList n'acceptant que des instances de Voiture ou de ses sous-classes
ArrayList<? extends Voiture> listVoitureSP = new ArrayList();
}
}
|
Et une application de ceci
consiste à faire des méthodes génériques, comme par exemple avoir une méthode
qui permette de lister toutes les valeurs de notre
ArrayList citée
précédemment. Voici :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<? extends Voiture> listVoitureSP = new ArrayList();
afficher(listVoitureSP);
}
/**
* Méthode générique !
* @param <T>
* @param list
*/
static void afficher(ArrayList<? extends Voiture> list){
for(Voiture v : list)
System.out.println(v.toString());
}
}
|
Je conçois bien que ceci est
un peu ardu à comprendre... Mais vous en aurez sûrement besoin dans une de vos
prochaines applications !
Bon
: je crois que nous avons fait un bon tour du sujet même si nous n'avons pas
tout abordé... Allez, le topo classique.
Ce qu'il faut retenir
- La générécité est apparue depuis le JDK 1.5.
- Ce concept est très utile pour développer des objets pouvant travailler
avec plusieurs types de données.
- Vous passerez donc moins de temps à développer des
classes pour traiter de façon identique des données différentes.
- La généricité permet de ré-utiliser le polymorphisme sans
risque avec les collections.
- Cela vous permet une meilleure robustesse du code.
- Vous pouvez coupler les collections avec la généricité !
- Le wildcard
(?) permet de dire que n'importe quel type peut être traité et donc
accepté !
- Vous pouvez élargir le champ d'acceptation d'une collection générique
grâce au mot clé extends.
- Pour ce genre de cas, les méthodes génériques sont
particulièrement adpatées et permettent d'utiliser le polymorphisme
dans toute sa splendeur !
J'espère que ce chapitre n'a été trop... lourd...
En
attendant, nous avons presque terminé cette seconde partie... La programmation
d'interface graphique se rapproche !
Mais il nous reste une dernière chose à
aborder qui peut s'avérer importante ! La
réflexivité.
Java et la réflexivité
Voici un chapitre qui, je pense, ne vous servira pas tout de
suite.
Cependant, il me semble important d'en parler...
La
réflexivité n'est en fait que le moyen de connaître toutes les informations
concernant une classe donnée. Vous pourrez même créer des instances de classe de
façon dynamique grâce à ceci.
Je pense faire une partie sur les
design pattern après celle sur les interfaces
graphiques ! Et, à ce moment, vous aurez sans doute besoin des connaissances de
ce chapitre, surtout lorsque nous aborderons le
pattern factory.
En attendant, je pense
que ce chapitre va tout de même vous intéresser !

Alors,
allons-y...
Commençons par le commencement
La
réflexivité, aussi appelée
introspection, consiste à découvrir de façon dynamique des
informations propres à une classe Java ou à un objet. Ceci est notamment utilisé
au niveau de la machine virtuelle Java lors de l'exécution de votre programme.
En gros, votre machine virtuelle stocke les informations relatives à une classe
dans un objet.
Concrètement, que se passe-t-il ?
Au
chargement d'une classe Java, votre
JVM crée automatiquement un objet.
Celui-ci récupère toutes les caractéristiques de votre classe ! Il s'agit d'un
objet
Class.
Exemple: si vous avez créé trois nouvelles classes
Java, à l'exécution de votre programme, la
JVM va créer un objet
Class pour chacune
d'elles.

Comme
vous devez vous en douter, cet objet possède une multitude de méthodes qui
permettent d'avoir tous les renseignements possibles et imaginables sur une
classe.
Dans ce chapitre, nous allons visiter la classe
String.
Créez un
nouveau projet ainsi qu'une classe contenant la méthode
main.
Voici deux façons de récupérer un objet
Class
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
public class Test {
public static void main(String[] args) {
Class c = String.class;
Class c2 = new String().getClass();
/*La fameuse méthode finale dont je vous parlais dans le chapitre sur l'héritage
Cette méthode vient de la classe Object
*/
}
}
|
Maintenant que vous savez
récupérer un objet
Class, nous allons tout de suite voir ce
qu'il sait faire !
Interroger un objet Class
Dans ce sous-chapitre, nous allons voir une partie des
choses que sait faire un objet
Class. Je ne vais pas tout vous montrer...
De toute façon, je pense que vous êtes à même de chercher et de trouver tous
seuls maintenant. Vous avez l'habitude de manipuler des objets, à
présent...
Connaître la super classe d'une
classe
Ce qui nous donne :
La
classe
Object n'a pas de super-classe...
Voyez plutôt :
Connaître la liste des
interfaces
Vous pouvez tester ce code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
public class Test {
public static void main(String[] args) {
//On récupère un objet Class
Class c = new String().getClass();
//La méthode getInterfaces retourne un tableau de Class
Class[] faces = c.getInterfaces();
//Pour voir le nombre d'interfaces
System.out.println("Il y a " + faces.length + " interfaces implémentées");
//On parcourt le tableau d'interfaces
for(int i = 0; i < faces.length; i++)
System.out.println(faces[i]);
}
}
|
Ce qui nous donne :
Connaître la liste des méthodes de
la classe
La méthode
getMethods() de l'objet
Class nous retourne un
tableau d'objets
Method présents dans le
package
java.lang.reflect.
Vous pouvez soit faire l'import à
la main, soit déclarer un tableau d'objets
Method et utiliser le raccourci
Ctrl + Shift + O.
Voici un code
qui retourne la liste des méthodes de la classe
String :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
Class c = new String().getClass();
Method[] m = c.getMethods();
System.out.println("Il y a " + m.length + " méthodes dans cette classe");
//On parcourt le tableau de méthodes
for(int i = 0; i < m.length; i++)
System.out.println(m[i]);
}
}
|
Voici un morceau du résultat
car, comme vous pourrez le voir, il y a beaucoup de méthodes dans la classe
String.
Vous
pouvez constater que l'objet
Method regorge lui aussi de méthodes intéressantes.
Voici un code qui affiche la liste des méthodes ainsi que la liste des attributs
qu'elles prennent :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 |
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
Class c = new String().getClass();
Method[] m = c.getMethods();
System.out.println("Il y a " + m.length + " méthodes dans cette classe");
//On parcourt le tableau de méthodes
for(int i = 0; i < m.length; i++)
{
System.out.println(m[i]);
Class[] p = m[i].getParameterTypes();
for(int j = 0; j < p.length; j++)
System.out.println(p[j].getName());
System.out.println("----------------------------------------\n");
}
}
}
|
Et voilà :
Connaître la liste des champs
(variable de classe ou d'instance) de la classe
Ici,
nous allons appliquer la même méthodologie que pour la liste des méthodes sauf
que cette fois, la méthode invoquée retourne un tableau d'objets
Field. Voici un code
qui affiche la liste des champs de la classe
String.
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) {
Class c = new String().getClass();
Field[] m = c.getFields();
System.out.println("Il y a " + m.length + " champs dans cette classe");
//On parcourt le tableau de méthodes
for(int i = 0; i < m.length; i++)
System.out.println(m[i].getName());
}
}
|
Connaître la liste des constructeurs
de la classe
Ici, nous utiliserons un objet
Constructor pour lister
les constructeurs de la classe :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 |
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) {
Class c = new String().getClass();
Constructor[] construc = c.getConstructors();
System.out.println("Il y a " + construc.length + " constructeurs dans cette classe");
//On parcourt le tableau des constructeur
for(int i = 0; i < construc.length; i++){
System.out.println(construc[i].getName());
Class[] param = construc[i].getParameterTypes();
for(int j = 0; j < param.length; j++)
System.out.println(param[j]);
System.out.println("------------------------------------\n");
}
}
}
|
Vous pouvez donc constater
que l'objet
Class regorge de méthodes en tout genre
!
Maintenant, si nous essayons d'exploiter un peu plus celles-ci...
Instanciation dynamique
Nous allons voir une petite partie de la puissance de
cette classe (pour l'instant).
Dans un premier temps, créez un nouveau projet
avec une méthode
main, ainsi qu'une
classe correspondant à ceci :
Voici
son code Java :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 |
public class Paire {
private String valeur1, valeur2;
public Paire(){
this.valeur1 = null;
this.valeur2 = null;
System.out.println("Instanciation ! !");
}
public Paire(String val1, String val2){
this.valeur1 = val1;
this.valeur2 = val2;
System.out.println("Instanciation avec des paramètres ! !");
}
public String toString(){
return "Je suis un objet qui a pour valeur : " + this.valeur1 + " - " + this.valeur2;
}
public String getValeur1() {
return valeur1;
}
public void setValeur1(String valeur1) {
this.valeur1 = valeur1;
}
public String getValeur2() {
return valeur2;
}
public void setValeur2(String valeur2) {
this.valeur2 = valeur2;
}
}
|
Le but du jeu maintenant
consiste à créer un objet
Paire
sans utiliser l'opérateur
new.
Pour instancier un nouvel
objet
Paire, nous allons tout
d'abord récupérer ses constructeurs. Ensuite, nous allons préparer un tableau
contenant les données à insérer. Puis nous invoquerons la méthode
toString().
Regardez comment procéder ;
par contre, il y a moultes exceptions :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 |
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) {
String nom = Paire.class.getName();
try {
//On crée un objet Class
Class cl = Class.forName(nom);
//Nouvelle instance de la classe Paire
Object o = cl.newInstance();
//On crée les paramètres du constructeur
Class[] types = new Class[]{String.class, String.class};
//On récupère le constructeur avec les deux paramètres
Constructor ct = cl.getConstructor(types);
//On instancie l'objet avec le constructeur surchargé !
Object o2 = ct.newInstance(new String[]{"valeur 1 ", "valeur 2"} );
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
|
Et le résultat donne :
Nous
pouvons maintenant appeler la méthode
toString() du deuxième objet... oh et soyons
fous, sur les deux :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 |
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
String nom = Paire.class.getName();
try {
//On crée un objet Class
Class cl = Class.forName(nom);
//Nouvelle instance de la classe Paire
Object o = cl.newInstance();
//On crée les paramètres du constructeur
Class[] types = new Class[]{String.class, String.class};
//On récupère le constructeur avec les deux paramètres
Constructor ct = cl.getConstructor(types);
//On instancie l'objet avec le constructeur surchargé !
Object o2 = ct.newInstance(new String[]{"valeur 1 ", "valeur 2"} );
//On va chercher la méthode toString, elle n'a aucun paramètre
Method m = cl.getMethod("toString", null);
//La méthode invoke exécute la méthode sur l'objet passé en paramètre,
// pas de paramètre, donc null en deuxième paramètre de la méthode invoke !
System.out.println("---------------------------------------------");
System.out.println("Méthode " + m.getName() + " sur o2: " +m.invoke(o2, null));
System.out.println("Méthode " + m.getName() + " sur o: " +m.invoke(o, null));
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
|
Et le résultat

:
Voilà
: nous venons de créer deux instances d'une classe sans passer par l'opérateur
new.
Mieux encore ! Car nous avons même pu appeler une méthode de nos instances !

Je
ne vais pas m'attarder trop longtemps sur ce sujet... Mais gardez en tête que
cette façon de faire, même si elle est très lourde, pourrait vous être utile. Et
là, je repense à mon
pattern
factory. En quelques mots, il s'agit d'une classe Java qui ne
fait que créer des instances !

Bon.
Je crois que vous avez bien mérité une pause. Les deux derniers chapitres ont
été assez éprouvants...
Un petit topo et en route pour la partie 3 !
Ce qu'il faut retenir
- Lorsque votre JVM interprète votre programme, elle crée automatiquement un
objet Class pour chaque classe chargée.
- Avec un tel objet, vous pouvez connaître absolument tout sur votre classe.
- L'objet Class utilise des sous-objets comme
Method,
Field,
Constructor... qui permettent de
travailler avec vos différents objets, mais aussi avec ceux présents dans
Java.
- Avec cet objet, vous pouvez créer des instances de vos classes Java sans
utiliser new.
- Par contre, vous devez savoir que les performances restent
médiocres.
Allez : je vous fais grâce d'un QCM... Une
fois n'est pas coutume !
Voilà, c'était le dernier chapitre de cette partie
!
Il y a eu pas mal de choses vues ici...
Je ne vais faire de long
discours maintenant que vous êtes si près de la programmation
événementielle...
Alors... Rendez-vous dans la troisième partie.
J'espère que cette partie vous a plu et que vous avez appris plein de
bonne choses !
J'ai volontairement omis de parler des flux et des threads dans cette partie. Je préfère avoir des
cas bien concrets à vous soumettre pour ça...
Bon : je sais que beaucoup
d'entre vous l'attendent avec impatience, alors voici la partie sur la
programmation événementielle
!
Partie 3 : Java et la programmation événementielle
Dans cette partie, nous aborderons la programmation événementielle comme le
stipule le titre.
Par là, entendez
programmation
d'interface graphique, ou
IHM, ou encore
GUI.
Nous utiliserons
essentiellement les bibliothèques
Swing et
AWT
présentes d'office dans Java.
Nous verrons ce qui
forme, je pense, les fondements de base ! Nous n'entrerons pas dans les détails,
enfin pas trop...

Je
ne vais pas faire de long discours maintenant, je sais que vous êtes
impatients... alors go !
Votre première fenêtre
Dans ce chapitre, nous allons apprendre à nous servir de
l'objet
JFrame, présent dans le package
Swing.
À la fin du chapitre, vous serez à même de créer une fenêtre, de choisir
sa taille...
Trêve de bavardage inutile, commençons tout de suite !
L'objet JFrame
Nous y voilà... Avant de nous lancer à corps perdu,
vous devez savoir ce que nous allons utiliser...
Vu que allons développer des
interfaces avec
swing, vous devez savoir que toutes
les classes
swing se trouvent dans le package
javax.swing. Mais ne vous y trompez pas,
nous allons utiliser aussi des objets
awt (java.awt), mais pas de composants
!
Pour faire simple, un composant peu être un bouton, une zône de texte, une
case à cocher... Bref, tout ce qui peut interagir avec vous !
Il est très fortement recommandé de ne pas
mélanger les composants swing et awt ! ! Ceci pour cause de conflit ! Si vous faites ceci
vous aurez de très grandes difficultés à faire une IHM stable et valide ! En
effet, pour faire simple, swing et awt se basent sur les mêmes fondements mais diffèrent sur
l'utilisation de ces fondements...
Pourquoi ?
Tout
simplement car les objets de ces deux package ne sont pas construit de la même
façon et des conflits peuvent survenir (superposition de
composants...).
Je ne vous demande pas de créer un projet avec une classe
main, celui-ci doit être prêt
depuis des lustres, facile !
Pour utiliser une fenêtre de type
JFrame,
vous devez instancier celui-ci. Comme ceci :
Code :
Java
1
2
3
4
5
6
7
8
9
10 |
import javax.swing.JFrame;
public class Test {
public static void main(String[] args){
JFrame fenetre = new JFrame();
}
}
|
Eh ! Lorsque j'exécute mon code, rien ne s'affiche
!
Oui, parce que par défaut, votre
JFrame n'est pas visible...
Pour pouvoir l'afficher à l'écran, vous devez lui dire "
sois visible", comme ça :
Code : Java
1
2
3
4
5
6
7
8
9
10 |
import javax.swing.JFrame;
public class Test {
public static void main(String[] args){
JFrame fenetre = new JFrame();
fenetre.setVisible(true);
}
}
|
Et lorsque vous exécuterez votre
code, vous pourrez voir ceci :
Eh, mais tu te moques de nous ! Elle est minuscule
!
Bienvenue dans le monde de la programmation événementielle ! Il faut
que vous vous y fassiez... Vos composants ne sont pas intelligents : il va
falloir leur dire tout ce qu'ils doivent faire !
Bon, pour avoir une
fenêtre plus conséquente, il faudrait :
- qu'elle soit plus grande ; en effet, c'est mieux ;
- qu'elle ait un titre (c'est pas du luxe !) ;
- si elle pouvait être centrée au milieu de mon écran, ce serait parfait !
Par contre, vous ne l'avez peut-être pas remarqué
mais, lorsque vous cliquez sur la croix rouge (pour fermer votre fenêtre), cette
action ne termine pas le processus dans Eclipse !
Pour réellement
terminer le processus de l'application, vous devrez ajouter une autre
instruction.
Pour chacune des choses que je viens d'énumérer, il y a aura
une méthode à appeler pour que votre
JFrame sache à quoi s'en tenir !
Voici
un code qui reprend toutes nos doléances :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
import javax.swing.JFrame;
public class Test {
public static void main(String[] args){
JFrame fenetre = new JFrame();
//Définit un titre pour votre fenêtre
fenetre.setTitle("Ma première fenêtre java");
//Définit une taille pour celle-ci ; ici, 400 px de large et 500 px de haut
fenetre.setSize(400, 500);
//Nous allons maintenant dire à notre objet de se positionner au centre
fenetre.setLocationRelativeTo(null);
//Terminer le processus lorsqu'on clique sur "Fermer"
fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fenetre.setVisible(true);
}
}
|
Et voici le résultat :
Par
contre, je pense qu'il vaudrait mieux que nous ayons notre propre objet. Comme
ça, on n'aura pas à redéfinir les attributs à chaque fois... Donc créons notre
propre classe !
Votre fenêtre héritée
Pour commencer, effacez tout le code que vous avez
écrit dans votre méthode
main.
Ensuite, créez une classe que nous appellerons "
Fenetre", et faites-la hériter de
JFrame.
Voilà le code de cette classe
pour le moment :
Code : Java
1
2
3
4
5 |
import javax.swing.JFrame;
public class Fenetre extends JFrame{
}
|
Nous allons maintenant créer
notre constructeur et, dans celui-ci, nous mettrons nos instructions à
l'intérieur.
Ce qui nous donne :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
import javax.swing.JFrame;
public class Fenetre extends JFrame{
public Fenetre(){
//Définit un titre pour votre fenêtre
this.setTitle("Ma première fenêtre java");
//Définit une taille pour celle-ci ; ici, 400 px de large et 500 px de haut
this.setSize(400, 500);
//Nous allons maintenant dire à notre objet de se positionner au centre
this.setLocationRelativeTo(null);
//Ferme-toi lorsqu'on clique sur "Fermer" !
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
}
|
Ensuite, vous avez le choix,
soit :
- vous conservez votre classe contenant la méthode main et vous créez une instance de Fenetre
- vous effacez cette classe, et vous mettez votre méthode main dans votre classe Fenetre. Vous devez tout de même créer
une instance de votre Fenetre...
Personnellement, je préfère sortir ma méthode
main dans une classe à part... Mais je ne vous force à
rien !

Quelque
soit l'emplacement de votre
main,
la ligne de code qui suit doit y figurer :
Code :
Java
1 |
Fenetre fen = new Fenetre();
|
Exécutez votre nouveau code
et... vous avez exactement la même chose que précédemment !
Vous conviendrez que c'est tout de même plus pratique de ne plus écrire les
même choses à chaque fois... Comme ça, vous avez une classe qui va se charger de
l'affichage de votre programme futur !
Faisons un léger tour d'horizon de
cette classe.
Des méthodes et encore des méthodes
Je vais vous faire une petite liste des méthodes que
vous serez susceptibles d'utiliser.
Positionner sa fenêtre à
l'écran
Déjà, nous avons centré notre fenêtre, mais
vous auriez peut-être voulu la positionner ailleurs. Pour faire ceci, vous avez
la méthode
setLocation(int x, int
y).
Avec cette méthode, vous pouvez spécifier où doit se
situer votre fenêtre sur votre écran. Les coordonnées, exprimées en pixels, sont
basées sur un repère prenant le coin supérieur gauche comme origine.
La
première valeur de la méthode vous positionne sur l'axe X, 0 correspondant à
l'origine ; les valeurs positives déplacent la fenêtre vers la droite, et les
valeurs négatives vous font sortir de l'écran vers la gauche. La même règle
s'applique pour les valeurs Y, excepté que les valeurs positives font descendre
la fenêtre en commençant par l'origine, et les valeurs négatives font sortir la
fenêtre par le haut !
Empêcher le redimensionnement de la
fenêtre
Pour faire ceci, il vous suffit d'invoquer la
méthode
setResizable(false);, et de
le repasser à
setResizable(true)
pour le rendre actif !
Faire que votre fenêtre soit
toujours au premier plan
Il s'agit là encore d'une
méthode qui prend un booléen en paramètre. Passer
true mettra votre fenêtre au premier plan quoi qu'il
advienne, et passer
false annulera le statut. Cette
méthode est
setAlwaysOnTop(boolean
b).
Retirer les contours et les boutons
de contrôles
Pour ce faire, il vous suffit d'utiliser
la méthode
setUndecorated(Boolean
b).
Je ne vais pas faire le tour de toutes les méthodes
maintenant... De toute façon, nous allons nous servir de pas mal d'autres dans
un futur très proche...
C'est bien joli tout ça, mais on aimerait bien pouvoir
mettre des trucs dans notre fenêtre !
Bien sûr : mais avant, il vous
faut encore apprendre une bricole... En fait votre fenêtre, telle qu'elle
apparaît, vous cache quelques petites choses...
Ce que vous cache votre fenêtre
Vous pensez, et c'est légitime, que votre fenêtre est
toute simple, dépourvue de tout composant (hormis les contours).
Eh bien vous
vous trompez !
Une
JFrame est découpée en plusieurs parties
:
- la fenêtre,
- le RootPane, le container (conteneur) principal qui contient les autres composants,
- le LayeredPane, qui forme
juste un panneau composé du ContentPane et de la
barre de menu (MenuBar),
- la MenuBar, la barre de menu
quand il y en a une...
- le ContentPane ; c'est dans
celui-ci que nous mettrons nos composants,
- et le GlassPane, couche
utilisée pour intercepter les actions de l'utilisateur avant qu'elles ne
parviennent aux composants.
Pas de panique...

Nous n'allons nous servir que du
contentPane et,
pour le récupérer, nous n'avons qu'à utiliser la méthode
getContentPane() de la classe
JFrame.
Cependant, nous allons
utiliser un composant autre que le
contentPane. Nous
utiliserons un
JPanel.
Il existe d'autres types de fenêtres. La
JWindow,
une JFrame sans bord et non draggable (déplaçable), et la JDialog, une fenêtre
non redimensionnable. Mais nous n'en parlerons pas ici...
Bon :
qu'est-ce qu'on attend ?
Ah oui ! Le topo et le QCM...
Ce qu'il faut retenir
- Les composants swing se trouvent dans javax.swing, et les
awt
dans java.awt.
- Il ne faut pas mélanger les composants swing et awt ! !
- Une JFrame est constituée de plusieurs
composants.
- Par défaut, une fenêtre a une taille minimale, et n'est pas visible.
- Un composant doit être bien paramétré pour qu'il fonctionne à notre
convenance.
J'ose espérer que ce premier chapitre était à votre
goût...
Il n'était pas trop difficile pour commencer. Mais les choses vont
vite se compliquer...
Continuons avec les
conteneurs.
Une histoire de conteneur
Dans cette partie, nous allons aborder la notion de
conteneur, et plus particulièrement le
JPanel (pour
l'instant...)
Vous verrez pas mal de choses qui vous seront très utiles plus
tard, enfin j'espère.
Mais surtout, à la fin de cette partie, nous pourrons
presque commencer à mettre des composants sur nos fenêtres...

Allez,
je vous sens impatients...
Créez un conteneur pour votre fenêtre
Comme je vous l'ai dit auparavant, nous allons utiliser
un
JPanel, composant de type
container, dont la vocation est d'accueillir d'autres
objets de même type, ou des objets de type composant (bouton, case à
cocher...).
Mais dans l'absolu, n'importe quel composant peut
accueillir un autre composant ! Nous verrons ça...
Voici la marche à suivre
- Nous allons importer la classe javax.swing.JPanel dans notre classe
héritée de JFrame.
- Nous allons instancier un JPanel, lui spécifier une couleur de
fond pour pouvoir mieux le voir !
- Dire à notre JFrame que ce sera notre JPanel qui sera son
contentPane.
Rien de
bien sorcier en somme. Qu'attendons-nous ?
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 |
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Fenetre extends JFrame {
public Fenetre(){
this.setTitle("Ma première fenêtre java");
this.setSize(400, 500);
this.setLocationRelativeTo(null);
//Instanciation d'un objet JPanel
JPanel pan = new JPanel();
//Définition de sa couleur de fond
pan.setBackground(Color.ORANGE);
//On prévient notre JFrame que ce sera notre JPanel qui sera son contentPane
this.setContentPane(pan);
this.setVisible(true);
}
}
|
Et voici le résultat :
C'est
un bon début, mais je vois que vous êtes frustrés car il n'y a pas beaucoup de
changement par rapport à la dernière fois...
Eh bien c'est maintenant que les choses deviennent intéressantes !
Avant
de vous faire utiliser des composants comme des boutons ou autre chose, nous
allons nous amuser avec notre panneau. Plus particulièrement avec un objet qui a
pour rôle de dessiner et de peindre notre composant... Ça vous tente ? Alors, Go
!

L'objet Graphics
Cet objet a une particularité de taille !
Vous
ne pouvez l'utiliser que si, et seulement si le système vous l'a donné
!Et pour bien comprendre le mode de fonctionnement de vos futurs
containers (ou composants), nous allons faire une classe héritée de
JPanel :
appelons-la
Panneau ; nous allons
faire petit tour d'horizon du fonctionnement de celle-ci !
Voici le code
de cette classe :
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
//Vous pourrez voir cette phrase à chaque fois que la méthode est invoquée !
System.out.println("Je suis exécutée ! ! !");
g.fillOval(20, 20, 75, 75);
}
}
|
Hop là ! Qu'est-ce que c'est que cette méthode
?
Cette méthode est celle que l'objet appelle pour se peindre sur notre
fenêtre, et si vous réduisez cette dernière et que vous l'affichez de nouveau,
c'est encore cette méthode qui est appelée pour afficher notre composant ! Idem
si vous redimensionnez votre fenêtre... En plus, on n'a même pas à redéfinir de
constructeur, cette méthode est appelée automatiquement !
Pour
personnaliser des composants, ceci est très pratique car vous n'aurez
JAMAIS à l'appeler de vous-mêmes,
ceci est automatique ! Tout ce que
vous pourrez faire, c'est forcer l'objet à se repeindre, mais ce n'est pas cette
méthode que vous invoquerez... Nous y reviendrons !
Vous aurez constaté
que cette méthode possède un argument et qu'il s'agit du fameux objet
Graphics
tant convoité. Nous reviendrons sur l'instruction
g.fillOval(20, 20, 75, 75); mais vous verrez ce
qu'elle fait lorsque vous exécuterez votre programme...

Et
maintenant, dans notre classe
Fenetre :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Fenetre extends JFrame {
public Fenetre(){
this.setTitle("Ma première fenêtre java");
this.setSize(100, 150);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setContentPane(new Panneau());
this.setVisible(true);
}
}
|
Exécutez
votre
main, et vous devriez avoir
ceci :
Une
fois votre fenêtre affichée, étirez-la, réduisez-la...
À présent, vous pouvez
voir ce qu'il se passe lorsque vous interagissez avec votre fenêtre !
Celle-ci met à jour ses composants à chaque changement d'état ou de
statut ! Et l'intérêt d'avoir une classe héritée d'un container ou d'un
composant, c'est que nous pouvons redéfinir la façon dont est peint ce composant
sur la fenêtre !
Donc, après cette mise en bouche... Si nous explorions
un peu plus les capacités de notre objet
Graphics ?
Plus loin dans le Graphics
Comme vous avez pu le voir, l'objet Graphics permet,
entre autres, de tracer des ronds... Mais cet objet possède tout un tas de
méthodes plus pratiques et amusantes les unes que les autres...
Nous ne les
verrons pas toutes mais vous aurez déjà de quoi faire...
Pour commencer,
reprenons la méthode que j'ai utilisée précédemment :
g.fillOval(20, 20, 75, 75);.
Si nous avions à
traduire cette instruction en français, ça donnerait :
"
Trace un rond plein en commençant à dessiner sur l'axe x à 20
pixels, sur l'axe y à 20 pixels, et fais en sorte que mon rond fasse 75 pixels
de large et 75 pixels de haut."
C'est simple à
comprendre, n'est-ce pas ?
Oui, mais si je veux que mon rond soit centré et qu'il
y reste ?
C'est dans ce genre de cas qu'il est intéressant d'avoir une
classe héritée !

Vu
que nous sommes dans notre objet
JPanel, nous avons accès à ses données et
j'ajouterais, pile au bon moment : lorsque nous allons le dessiner !
En
effet, il y a des méthodes dans les objets composants qui nous retournent sa
largeur (
getWidth()) et sa hauteur
(
getHeight()) !
Par contre,
réussir à centrer un rond dans un
JPanel en toute
circonstance demande un peu de calcul mathématique de base, une pincée de
connaissances et un soupçon de logique !

Reprenons
notre fenêtre telle qu'elle est en ce moment. Vous pourrez constater que les
coordonnées de départ ne correspondent pas au départ du cercle en lui-même, mais
au point de départ du carré qui entoure ce cercle !
Ceci
signifie que, si nous voulons que notre cercle soit centré à tout moment, il
faut que notre carré soit centré et donc, que le centre de celui-ci corresponde
au centre de notre fenêtre ! J'ai essayé de faire un schéma représentant ce que
nous devons obtenir.
Ainsi,
le principe est de prendre la largeur et la longueur de notre composant ainsi
que la largeur et la longueur du carré qui englobe notre rond ! Facile, jusqu'à
présent...
Maintenant, pour trouver où se situe le point où doit commencer le
dessin, il faut prendre la moitié de la largeur de notre composant, moins la
moitié de la largeur de notre rond, tout ceci pour l'axe x et y.
Pour que notre rond soit le plus optimisé, nous
allons prendre pour taille de notre carré la moitié de notre fenêtre
!
Donc, pour simplifier le tout, nous nous retrouvons à calculer la
moitié de la moitié de la largeur et de la hauteur... Ce qui revient, au final,
à diviser la largeur et la hauteur par 4...
Voici le code qui fait ceci :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
int x1 = this.getWidth()/4;
int y1 = this.getHeight()/4;
System.out.println("largeur = " + this.getWidth() + ", longueur = " + this.getHeight());
System.out.println(" coordonnée de début d'affichage x1 = " + x1 + " y1 = " + y1);
g.fillOval(x1, y1, this.getWidth()/2, this.getHeight()/2);
}
}
|
Ce qui nous donne :
Bon,
l'objet
Graphics sait plein d'autres choses :
peindre des ronds vides, par exemple.

Sans
rire... Maintenant que vous avez vu un peu comment fonctionne cet objet, nous
allons utiliser ses méthodes...
La méthode
drawOval(int x1, int y1, int width, int
height)Il s'agit de la méthode qui permet de
dessiner un rond vide. Celle-ci fonctionne exactement de la même manière que la
méthode
fillOval.
Voici un code
mettant en oeuvre cette méthode :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13 |
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
int x1 = this.getWidth()/4;
int y1 = this.getHeight()/4;
g.drawOval(x1, y1, this.getWidth()/2, this.getHeight()/2);
}
}
|
Résultat :
Si vous spécifiez une largeur différente de la
hauteur, ces méthodes dessineront une forme ovale !
La méthode
drawRect(int x1, int y1, int width, int
height)Cette méthode permet de dessiner des
rectangles vides. Bien sûr, son homologue
fillRect existe. Ces deux méthodes fonctionnent
aussi comme les précédentes, voyez plutôt ce code :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11 |
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
g.drawRect(10, 10, 50, 60);
g.fillRect(65, 65, 30, 40);
}
}
|
Et le résultat :
La méthode
drawRoundRect(int x1, int y1, int width, int height, int arcWidth, int
arcHeight)Il s'agit de la même chose que
précédemment, mis à part que le rectangle sera arrondi. Arrondi défini par les
valeurs passées dans les deux derniers paramètres.
Code
: Java
1
2
3
4
5
6
7
8
9
10
11 |
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
g.drawRoundRect(10, 10, 30, 50, 10, 10);
g.fillRoundRect(55, 65, 55, 30, 5, 5);
}
}
|
Résultat :
La méthode
drawLine(int x1, int y1, int x2, int
y2)Celle-ci vous permet de tracer des lignes
droites ! Il vous suffit de lui spécifier les coordonnées de départ et d'arrivée
de la ligne... Simple aussi, n'est-ce pas ?
Dans ce code, je trace les
diagonales de notre conteneur :
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
g.drawLine(0, 0, this.getWidth(), this.getHeight());
g.drawLine(0, this.getHeight(), this.getWidth(), 0);
}
}
|
Résultat :
La méthode
drawPolygon(int[] x, int[] y, int nbrePoints)Avec cette
méthode, vous pourrez dessiner des polygones de votre composition. Eh oui...
C'est à vous de définir les coordonnées de tous les points qui forment votre
polygone !

Le
dernier paramètre de cette méthode est le nombre de points formant votre
polygone. Ainsi, vous ne serez pas obligés de créer deux fois le point d'origine
pour boucler votre figure. Java fermera celle-ci automatiquement en reliant le
dernier point de votre tableau au premier...

Je
vous conseille vivement de faire un schéma pour vous aider... Cette méthode a
aussi son homologue pour dessiner les polygones remplis :
fillPolygon.
Code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
int x[] = {20, 30, 50, 60, 60, 50, 30, 20};
int y[] = {30, 20, 20, 30, 50, 60, 60, 50};
g.drawPolygon(x, y, 8);
int x2[] = {50, 60, 80, 90, 90, 80, 60, 50};
int y2[] = {60, 50, 50, 60, 80, 90, 90, 80};
g.fillPolygon(x2, y2, 8);
}
}
|
Résultat :
Vous
avez aussi une méthode qui prend exactement les mêmes arguments et qui, elle,
trace plusieurs lignes ! Cette méthode s'appelle :
drawPolyline(int[] x, int[]y, int
nbrePoints).
Cette méthode va dessiner les lignes
correspondant aux coordonnées que vous lui passerez dans les tableaux, sachant
que lorsque vous passez à l'indice supérieur dans vos tableaux, la méthode prend
automatiquement les valeurs de l'indice précédent comme point d'origine.
Cette dernière ne fait pas le lien entre la première et la dernière
valeur de vos tableaux... Vous pouvez essayer le code précédent, en remplaçant
drawPolygon par cette méthode et
vous verrez...
La méthode
drawString(String str, int x, int y)Voici la méthode qui
vous permet d'écrire du texte... Elle est très simple à comprendre puisqu'il
vous suffit de lui passer la phrase à écrire et de lui spécifier à quelles
coordonnées commencer !
Code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
g.drawString("Tiens ! le Site du Zér0 ! ! !", 10, 20);
}
}
|
Résultat :
Vous
pouvez modifier la couleur (et ça s'applique aussi pour les autres méthodes) et
la police d'écriture... Pour redéfinir la police d'écriture, vous devez créer un
objet
Font. Regardez comment faire
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
Font font = new Font("Courier", Font.BOLD, 20);
g.setFont(font);
g.setColor(Color.red);
g.drawString("Tiens ! le Site du Zér0 ! ! !", 10, 20);
}
}
|
Et le résultat :
La méthode
drawImage(Image img,int x, int y, Observer
obs);Ici, vous devrez charger votre image grâce à
trois objets :
- un objet Image
- un objet ImageIO
- un objet File.
Vous allez voir que
l'utilisation de ces objets est très simple... Nous déclarons un objet de type
Image,
nous allons l'initialiser en utilisant une méthode statique de l'objet
ImageIO,
qui, elle, prend un objet
File en paramètre. Ça paraît compliqué
comme ça, mais vous allez voir...
Par contre, notre image sera stockée à
la racine de notre projet !Et en ce qui concerne le dernier
paramètre de notre méthode
drawImage, il s'agit de l'objet qui est censé
observer l'image. Ici, nous allons mettre notre objet
Panneau, donc
this.
Avec cette méthode, l'image sera dessinée avec ses
propres dimensions... Si vous voulez que l'image prenne l'intégralité de votre
container, il faut utiliser le constructeur suivant : drawImage(Image img, int x,
int y, int width, int height,
Observer obs).
Code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 |
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
try {
Image img = ImageIO.read(new File("images.jpg"));
g.drawImage(img, 0, 0, this);
//Pour une image de fond
//g.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
|
Voici
les résultats selon ce que vous avez choisi :
Maintenant,
je pense qu'il est temps de vous présenter le petit cousin de notre objet
Graphics
!

Le cousin caché : l'objet Graphics2D
Voici une amélioration de l'objet
Graphics, et vous allez
vite comprendre pourquoi...
Pour utiliser cet objet, il nous suffit de
caster l'objet
Graphics en
Graphics2D, et surtout
de ne pas oublier d'importer sa classe !
Celle-ci se trouve dans le package
java.awt.
Voilà notre début de code
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class Panneau extends JPanel{
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D) g;
}
}
|
L'une des possibilités
qu'offre cet objet n'est autre que la possibilité de peindre des objets avec des
dégradés de couleurs...
Cette opération n'est pas du tout difficile à
effectuer. Pour ce faire, il vous suffit d'utiliser un objet
GradientPaint et une
méthode de l'objet
Graphics2D.
Nous n'allons pas
reprendre tous les cas que nous avons vus jusqu'à présent... Juste deux ou trois
pour que vous voyez bien la différence.
Commençons par notre objet
GradientPaint, voici comme l'initialiser
:
Code : Java
1 |
GradientPaint gp = new GradientPaint(0, 0, Color.RED, 30, 30, Color.cyan, true);
|
Vous devrez mettre à jour vos imports... Vous devez
ajouter ici : import java.awt.GradientPaint;
Alors, que
veut dire tout ceci ? Voici le détail du constructeur utilisé ici :
- paramètre 1 : la coordonnée x où doit commencer la couleur 1 ;
- paramètre 2 : la coordonnée y où doit commencer la couleur 1 ;
- paramètre 3 : couleur 1 ;
- paramètre 4 : la coordonnée x2 où doit commencer la couleur 2 ;
- paramètre 5 : la coordonnée y2 où doit commencer la couleur 2 ;
- paramètre 6 : couleur 2 ;
- paramètre 7 : booléen pour définir si le dégradé doit se répéter.
Entre ces deux points se créera le dégradé des deux
couleurs spécifiées !
Ensuite, pour utiliser ce dégradé dans une forme,
il suffit de mettre à jour votre objet
Graphics2D, comme ceci :
Code : Java
1
2 |
GradientPaint gp = new GradientPaint(0, 0, Color.RED, 30, 30, Color.cyan, true);
g2d.setPaint(gp);
|
Le code entier de cet
exemple est :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 |
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.RED, 30, 30, Color.cyan, true);
g2d.setPaint(gp);
g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
}
}
|
Voici les résultats obtenus,
l'un avec le booléen à
true, et l'autre à
false :
Votre
dégradé est oblique (rien ne m'échappe, à moi...). Ce sont les coordonnées
choisies qui influent sur la direction du dégradé. Dans notre exemple, nous
partons du point de coordonnées (0, 0) vers le point de coordonnées (30, 30). Si
vous vouliez un dégradé vertical, il faut juste mettre la valeur de x2 à 0 et
voilà :
Un
petit cadeau :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 |
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.RED, 20, 0, Color.magenta, true);
GradientPaint gp2 = new GradientPaint(20, 0, Color.magenta, 40, 0, Color.blue, true);
GradientPaint gp3 = new GradientPaint(40, 0, Color.blue, 60, 0, Color.green, true);
GradientPaint gp4 = new GradientPaint(60, 0, Color.green, 80, 0, Color.yellow, true);
GradientPaint gp5 = new GradientPaint(80, 0, Color.yellow, 100, 0, Color.orange, true);
GradientPaint gp6 = new GradientPaint(100, 0, Color.orange, 120, 0, Color.red, true);
g2d.setPaint(gp);
g2d.fillRect(0, 0, 20, this.getHeight());
g2d.setPaint(gp2);
g2d.fillRect(20, 0, 20, this.getHeight());
g2d.setPaint(gp3);
g2d.fillRect(40, 0, 20, this.getHeight());
g2d.setPaint(gp4);
g2d.fillRect(60, 0, 20, this.getHeight());
g2d.setPaint(gp5);
g2d.fillRect(80, 0, 20, this.getHeight());
g2d.setPaint(gp6);
g2d.fillRect(100, 0, 40, this.getHeight());
}
}
|

Quelques
exemples de dégradés avec d'autre formes :
Avec un cercle
Code
: Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 |
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(20, 20, Color.yellow, 95, 95, Color.blue, true);
g2d.setPaint(gp);
g2d.fillOval(this.getWidth()/4, this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
}
}
|
Avec un rectangle arrondi
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 |
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(20, 20, Color.green, 55, 75, Color.orange, true);
g2d.setPaint(gp);
g2d.fillRoundRect(30, 30, 75, 75, 10, 10);
}
}
|
Un peu de texte pour finir
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 |
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
g.setColor(Color.BLACK);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.cyan, 30, 30, Color.pink, true);
g2d.setPaint(gp);
Font font = new Font("Comics Sans MS", Font.BOLD, 14);
g2d.setFont(font);
g2d.drawString("On s'amuse les ZérOs !", 10, 15);
gp = new GradientPaint(-40, -40, Color.red, 100, 100, Color.white, false);
g2d.setPaint(gp);
font = new Font("Arial Black", Font.PLAIN, 16);
g2d.setFont(font);
g2d.drawString("Moi j'aime bien...", 10, 65);
}
}
|
Nous
avons fait un bon petit tour, là...
Pour les curieux, je vous conseille
d'aller voir la page des
tutos de Sun
Microsystems sur l'objet
Graphics2D.
Bon, je pense qu'un
topo serait le bienvenu...

Ce qu'il faut retenir
- L'objet JPanel se trouve dans le package
javax.swing.
- Un JPanel
peux contenir des composants ou d'autres containers ! !
- Lorsque vous ajoutez un JPanel principal à votre fenêtre,
n'oubliez pas de dire à votre fenêtre que ce dernier sera son contentPane.
- Pour redéfinir la façon dont l'objet est dessiné sur notre fenêtre, nous
devons utiliser la méthode paintComponent en créant une classe héritée.
- Cette méthode prend un paramètre, un objet Graphics.
- Cet objet doit nous être fourni par le système !
- C'est lui que nous allons utiliser pour dessiner dans notre container.
- Pour des dessins plus évolués, vous devrez utiliser l'objet Graphics2D qui
s'obtient en faisant un cast sur l'objet Graphics.
Et voilà, encore une partie rondement
menée.
Maintenant que nous savons comment ajouter un conteneur sur notre
fenêtre, nous allons voir comment positionner des composants !
Eh oui :
si vous ne dites pas à vos composants où ils doivent aller, ils se mettront à la
queue-leu-leu !
Bon : on y va ?
C'est parti pour : Faire une animation simple.
Faire une animation simple
Dans ce chapitre, nous allons voir comment créer une
animation simple.
Vous ne pourrez pas faire de jeu à la fin, mais je
pense que vous aurez de quoi vous amuser un peu...
Let's go alors...
Les déplacements : principe
Voilà le compte rendu de ce que nous avons :
- une classe héritée de JFrame
- une classe héritée de JPanel dans laquelle nous faisons de
zolis dessins. Un rond en l'occurrence...
Avec ces deux
classes, nous allons pouvoir créer un effet de déplacement.
Vous avez bien
entendu

: j'ai dit un
effet de déplacement
!
En réalité, le principe réside dans le fait que vous allez
donner des coordonnées différentes à votre rond, et vous allez forcer votre
objet
Panneau à se redessiner ! Tout ceci,
vous l'aviez deviné, dans une boucle !
Nous allons
donc nous préparer à ces nouveautés !
Jusqu'à présent, nous avons utilisé des
valeurs fixes pour les coordonnées de notre rond, et il va falloir dynamiser
tout ça...

Nous
allons donc créer deux variables privées de type
int dans notre classe
Panneau :
appelons-les
posX et
posY.
Pour l'animation que nous allons
travailler, notre rond devra provenir de l'extérieur de notre fenêtre. Partons
du principe que celui-ci va faire 50 pixels de diamètre : il faudra donc que
notre panneau peigne ce rond en dehors de sa zone d'affichage, nous
initialiserons donc nos deux variables d'instance à
-50.
Voilà à quoi ressemble notre classe, maintenant :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 |
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
private int posX = -50;
private int posY = -50;
public void paintComponent(Graphics g){
g.setColor(Color.red);
g.fillOval(posX, posY, 50, 50);
}
public int getPosX() {
return posX;
}
public void setPosX(int posX) {
this.posX = posX;
}
public int getPosY() {
return posY;
}
public void setPosY(int posY) {
this.posY = posY;
}
}
|
Il ne nous reste plus qu'à
faire en sorte que notre rond se déplace : il nous faut donc un moyen de changer
les coordonnées de celui-ci, le tout dans une boucle. Nous allons ainsi ajouter
une méthode privée dans notre classe
Fenetre afin de gérer tout cela ; nous
appellerons celle-ci en dernier dans notre constructeur. Voici donc à quoi
ressemble notre classe
Fenetre :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 |
import java.awt.Dimension;
import javax.swing.JFrame;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setContentPane(pan);
this.setVisible(true);
go();
}
private void go(){
for(int i = -50; i < pan.getWidth(); i++)
{
int x = pan.getPosX(), y = pan.getPosY();
x++;
y++;
pan.setPosX(x);
pan.setPosY(y);
pan.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
|
Hep ! Qu'est-ce que c'est que ces deux instructions à
la fin de la méthode go()
?
Tout d'abord, je pense que, les deux dernières instructions mises
à part, vous ne devez pas avoir trop de mal à comprendre ce code.
La
première des deux nouvelles instructions est
pan.repaint(). Cette dernière donne l'ordre à
votre composant, ici un
JPanel,
de se
redessiner.
La toute première fois, dans le constructeur de notre
classe
Fenetre, votre composant invoque la
méthode
paintComponent et dessine
un rond aux coordonnées que vous lui avez spécifiées. La méthode
repaint() ne fait rien d'autre que de faire à
nouveau appel à la méthode
paintComponent ; mais avant, vous avez changé les
coordonnées du rond par le biais des accesseurs créés précédemment. Donc à
chaque tour de boucle, les coordonnées de notre rond vont changer.
La
deuxième instruction est en fait un moyen de
faire une pause dans votre
code...

Celle-ci
met en attente votre programme pendant un laps de temps défini dans la méthode
sleep(), ce temps est exprimé en
millièmes de secondes (plus le temps d'attente est court, plus votre animation
sera rapide

).
Thread
est en fait un objet qui permet de créer un nouveau processus dans un programme,
ou de gérer le processus principal.
Dans tous les programmes,
il y a
au moins un processus, celui qui est en cours d'exécution. Mais vous
verrez plus tard qu'il est possible de diviser certaines tâches en plusieurs
processus afin de ne pas avoir de perte de temps et de performances dans vos
programmes. Pour le moment, sachez que vous pouvez faire des pauses dans vos
programmes avec cette instruction :
Code : Java
1
2
3
4
5
6
7 |
try{
Thread.sleep(1000);//Ici une pause d'une seconde
}catch(InterruptedException e) {
e.printStackTrace();
}
|
Cette instruction est dite "à risque", vous devez
donc l'entourer d'un bloc try{}catch(){} afin de capturer les exceptions
potentielles ! Sinon : ERREUR DE COMPILATION
!
Maintenant que toute la lumière est faite sur cette
affaire, exécutez votre code, et vous obtenez :
Bien
sûr, cette image est le résultat final, vous devriez avoir vu votre rond bouger
mais au lieu d'être clair, il a laissé une trainée derrière lui...
Pourquoi ?
C'est simple : vous avez demandé à
votre objet
Panneau de se redessiner, mais il a gardé
les précédents passages de votre rond sur lui-même ! Pour résoudre ce problème,
il suffit d'effacer ceux-ci avant de redessiner votre rond.
Comment fait-on ça ?
Il vous suffit de
dessiner un rectangle, d'une quelconque couleur, prenant toute la surface
disponible, avant de dessiner votre rond. Voici le code de notre classe
Panneau
mis à jour :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 |
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
private int posX = -50;
private int posY = -50;
public void paintComponent(Graphics g){
//On décide d'une couleur de fond pour notre rectangle
g.setColor(Color.white);
//On dessine celui-ci afin qu'il prenne tout la surface
g.fillRect(0, 0, this.getWidth(), this.getHeight());
//On redéfinit une couleur pour notre rond
g.setColor(Color.red);
//On le dessine aux coordonnées souhaitées
g.fillOval(posX, posY, 50, 50);
}
public int getPosX() {
return posX;
}
public void setPosX(int posX) {
this.posX = posX;
}
public int getPosY() {
return posY;
}
public void setPosY(int posY) {
this.posY = posY;
}
}
|
Voici trois captures d'écran
prises à différents moments de l'animation :
Je
pense qu'il est temps d'améliorer encore notre animation... Est-ce que ça vous
dirait que celle-ci continue tant que vous ne fermez pas votre fenêtre ?

Oui
? Alors continuons.

Continue, ne t'arrêtes pas si vite !
Voici l'un des moments délicats que j'attendais... Si
vous vous rappelez bien ce que je vous ai dit sur le fonctionnement des boucles,
vous devez vous souvenir de mon avertissement sur les boucles infinies !

Eh
bien ce que nous allons faire ici, c'est un exemple d'utilisation d'une boucle
infinie...

Si
vous y réfléchissez deux secondes, comment dire à une boucle de ne pas s'arrêter
à moins qu'elle ne s'arrête jamais ?
Dans l'exemple que nous allons utiliser pour le
moment, nous allons simplifier les choses, mais nous améliorerons cela lorsque
nous commencerons à interagir avec notre application...
Il y a
plusieurs manières de faire une boucle infinie : vous avez le choix entre une
boucle
for, while ou
do...while. Regardez ces déclarations
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
//Exemple avec un boucle while
while(true){
// Ce code se répètera à l'infini car la condition est TOUJOURS vraie !
}
//Exemple avec une boucle for
for(;;)
{
// Idem que précédemment, ici il n'y a pas d'incrément => donc la boucle ne se terminera jamais
}
//Exemple avec do...while
do{
//Encore une boucle que ne se terminera pas !
}while(true);
|
Nous allons donc remplacer
notre boucle finie par une boucle infinie dans la méthode
go() de notre objet
Fenetre. Ce qui nous
donne :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 |
private void go(){
for(;;)
{
int x = pan.getPosX(), y = pan.getPosY();
x++;
y++;
pan.setPosX(x);
pan.setPosY(y);
pan.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
|
Par contre, si vous avez
exécuté notre nouvelle version, vous avez dû vous rendre compte qu'il reste un
problème à gérer ! Eh oui. Votre rond ne se replace pas au départ lorsqu'il
atteint l'autre côté de notre fenêtre.
Si vous ajoutez une instruction System.out.println() dans la méthode
paintComponent inscrivant les
coordonnées de notre rond, vous devez voir que celles-ci ne cessent de croître !
Le premier objectif est atteint mais il nous reste à gérer ce
dernier problème.
Il faut donc réinitialiser les coordonnées de notre rond
si celles-ci arrivent au bout de notre composant.
Voici donc notre méthode
go() revue et corrigée
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 |
private void go(){
for(;;)
{
int x = pan.getPosX(), y = pan.getPosY();
x++;
y++;
pan.setPosX(x);
pan.setPosY(y);
pan.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Si nos coordonnées arrivent aux bords de notre composant
//On réinitialise
if(x == pan.getWidth() || y == getHeight())
{
pan.setPosX(-50);
pan.setPosY(-50);
}
}
}
|
Yes !

Le
code fonctionne parfaitement. En tout cas, comme nous l'avions prévu !
Mais
avant de passer au chapitre suivant, nous pouvons encore faire mieux...

On
y va ?
Attention aux bords, ne va pas te faire mal...
Maintenant, nous allons faire en sorte que notre rond
puisse détecter les bords de notre
Panneau et ricoche sur ceux-ci !

Vous
devez vous imaginer un code monstrueux, et vous êtes très loin du compte...
Tout d'abord, jusqu'à maintenant, nous n'attachions aucune importance
sur le bord que notre rond dépassait, ceci est terminé. Dorénavant, nous
séparerons le dépassement des coordonnées
posX
et
posY de notre
Panneau.
Mais comment lui dire qu'il faut reculer ou avancer
sur tel ou tel axe ?
Pour les instructions qui vont suivre, gardez en
mémoire que les coordonnées de notre rond sont en fait les coordonnées du
coin supérieur gauche du carré entourant notre rond !
!
Voilà la marche à suivre :
- si la coordonnée x de notre rond est inférieure à la largeur et qu'il
avance, on continue d'avancer ;
- sinon, on recule.
Et nous allons faire de même pour la
coordonnée y.

Comment
savoir si on doit avancer ou reculer ? Avec un booléen.

Au
tout début de notre application, deux booléens seront initialisés à
false et
si la coordonnée x est supérieure à la largeur du
Panneau, alors on
recule ; sinon, on avance, idem pour la coordonnée y.
Dans ce code, j'utilise deux variables de type
int pour
éviter de rappeler les méthodes getPosX() et getPosY().
Voilà notre nouveau code
de la méthode
go() :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 |
private void go(){
//Les coordonnées de départ de notre rond
int x = pan.getPosX(), y = pan.getPosY();
//Le booléen pour savoir si on recule ou non sur l'axe X
boolean backX = false;
//Le booléen pour savoir si on recule ou non sur l'axe Y
boolean backY = false;
//Pour cet exemple, j'utilise une boucle while
//Vous verrez qu'elle fonctionne très bien
while(true){
//Si la coordonnée x est inférieure à 1, on avance
if(x < 1)backX = false;
//Si la coordonnée x est supérieure à la taille du Panneau
//moins la taille du rond, on avance
if(x > pan.getWidth()-50)backX = true;
//idem pour l'axe Y
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
//Si on avance, on incrémente la coordonnée
if(!backX)
pan.setPosX(++x);
//Sinon, on décrémente
else
pan.setPosX(--x);
//Idem pour l'axe Y
if(!backY)
pan.setPosY(++y);
else
pan.setPosY(--y);
//On redessine notre Panneau
pan.repaint();
//Comme on dit : la pause s'impose ! Ici, 3 millièmes de secondes
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
|
Exécutez votre application
et vous devez voir que votre rond ricoche contre les bords de notre
Panneau.
Vous pouvez même étirer la fenêtre, la réduire et ça marche toujours !

On
commence à faire des choses sympa, non ?
Un petit topo vous attend avant un
petit QCM, et nous allons passer à la suite !

Ce qu'il faut retenir
- À l'instanciation d'un composant, la méthode paintComponent est automatiquement appelée.
- Vous pouvez forcer un composant à se redessiner en invoquant la méthode
repaint().
- Pensez bien à ce que va donner votre composant après être redessiné.
- Pour éviter que votre animation bave, réinitialisez le fond de votre
composant pour éviter ce phénomène...
- Vous verrez que tous les composants fonctionnent de la même manière.
- L'instruction Thread.sleep(),
permet de faire une pause dans votre programme.
- Cette méthode prend un entier comme paramètre qui correspond à un temps
exprimé en millièmes de secondes.
- Vous pouvez utiliser des boucles infinies pour faire des animations.
Encore un chapitre rondement mené !
Maintenant, je
pense que vous êtes paré pour : Votre premier
bouton.
Votre premier bouton
Voici l'un des moments que vous attendiez avec impatience
!
Vous allez enfin pouvoir mettre un bouton dans votre application...

Mais
ne chantez pas trop vite. Vous allez effectivement utiliser un bouton, mais voir
aussi que les choses se compliquent dès que vous utilisez ce genre de
composants... Et encore plus lorsque vous en utilisez plusieurs
!
Toujours prêts ?
Go, alors.
Utiliser la classe JButton
Comme le titre l'indique, nous allons utiliser un objet
JButton.
Celui-ci se trouve aussi dans le package
javax.swing.
Au cours de ce
chapitre, nous allons un peu mettre de côté notre objet
Panneau : en fait,
notre projet précédent dans sa globalité sera mis à l'écart...

Créez
un nouveau projet avec :
- une classe contenant une méthode main :
appelons-la Test ;
- une classe héritée de JFrame (avec tout ce
qu'on a déjà mis avant, sauf la méthode go()) : appelons-la Fenetre.
Je sais, j'aime donner
dans l'originalité...

Dans
votre classe
Fenetre, nous allons
créer une variable d'instance de type
JPanel et une de type
JButton.
Faites de votre
JPanel le
contentPane
de votre
Fenetre. Ce qui doit nous
donner ceci :
Classe Fenetre
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 |
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Fenetre extends JFrame{
private JPanel pan = new JPanel();
private JButton bouton = new JButton();
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setContentPane(pan);
this.setVisible(true);
}
}
|
Classe Test
Code : Java
1
2
3
4
5
6
7 |
public class Test {
public static void main(String[] args){
Fenetre fen = new Fenetre();
}
}
|
Jusque-là, rien de bien
nouveau mis à part l'instance de
JButton, mais ce genre de choses ne doit
plus vous épater, maintenant !
Définissons à présent un libellé à notre bouton et mettons-le sur...
Sur... Ce qui nous sert de
contentPane ! Notre
JPanel,
en l'occurrence !

Pour
donner un libellé à un bouton, il existe deux méthodes, les voici :
Code : Java
1
2
3
4
5 |
//Méthode 1 : Instanciation avec le libellé
JButton bouton = new JButton("Mon bouton");
//Méthode 2 : Instanciation puis définition du libellé
JButton bouton2 = new JButton();
bouton2.setText("Mon deuxième bouton");
|
Personnellement, je préfère
la première... Mais là n'est pas la question.

Il
ne nous reste plus qu'à ajouter notre bouton sur notre contentPane et ceci,
grâce à la méthode
add() de l'objet
JPanel.
Voici donc notre code
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 |
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Fenetre extends JFrame{
private JPanel pan = new JPanel();
private JButton bouton = new JButton("Mon bouton");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
//Ajout du bouton à notre contentPane
pan.add(bouton);
this.setContentPane(pan);
this.setVisible(true);
}
}
|
Et le résultat :
Alors
! Heureux ?

Je
ne sais pas si vous avez remarqué mais...
votre bouton est centré sur
votre conteneur !Ceci vous semble normal ? Sachez tout de même que
votre
JPanel fait de la mise en page par défaut.
Qu'est-ce que tu entends par là ?
En fait, en
java, il existe des objets qui servent à agencer vos composants, et les objets
JPanel en
ont un par défaut !
Pour vous le prouver, je vais vous faire travailler sur
le
contentPane de votre
JFrame.
Quoi ? On peut faire ça ?
Bien sûr, mais il
était de bon ton de vous faire découvrir l'objet
Graphics avant de vous
jeter dans la fosse aux lions...
Vous allez voir que pour obtenir le même
rendu que précédemment sur le
contentPane de votre
JFrame,
nous allons être obligés d'utiliser un de ces fameux objets d'agencements !

Tout
d'abord, pour utiliser le
contentPane d'une
JFrame,
on doit appeler la méthode
getContentPane(), qui retourne ce dernier, auquel
nous ajoutons nos composants. Voici le nouveau code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 |
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Fenetre extends JFrame{
private JPanel pan = new JPanel();
private JButton bouton = new JButton("Mon bouton");
public Fenetre(){
this.setTitle("Bouton");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
//On ajoute le bouton au contentPane de la JFrame
this.getContentPane().add(bouton);
this.setVisible(true);
}
}
|
Voyez que le résultat n'est
pas du tout le même :
Votre
bouton est ÉNORME ! En fait, il prend toute la place disponible, et ceci parce
que le
contentPane de votre
JFrame ne possède pas
d'objet d'agencement...
Ces fameux objets, dont vous ne pourrez plus vous
passer, s'appellent des layout
managers.
Faisons un petit tour d'horizon de ces objets
et voyons comment ils fonctionnent !
Les layout managers
Bon, vous allez voir qu'il existe plusieurs sortes de
layout managers et que selon votre
choix, il sera plus ou moins facile à utiliser... Je vais être méchant

. Nous verrons le plus simple en dernier !
Vous devez aussi savoir que tous
ces layout managers se trouvent dans la package :
java.awt.
L'objet
BorderLayoutLe
premier que nous aborderons est le
BorderLayout. Celui-ci est très pratique
si vous voulez placer vos composants de façon simple par rapport à une position
cardinale de votre conteneur. Si je
parle de positionnement cardinal, c'est parce que vous allez utiliser les
positions :
- NORTH
- SOUTH
- EAST
- WEST
- CENTER.
Mais je sais qu'un
aperçu vaut mieux qu'un exposé sur le sujet. Alors voilà un exemple mettant en
oeuvre un
BorderLayout.
Cette
fenêtre est composée de cinq
JButton positionnés aux cinq endroits
différents qu'offre un
BorderLayout.
Voici le code de
cette fenêtre :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 |
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Fenetre extends JFrame{
private JButton bouton = new JButton("BorderLayout.NORTH");
public Fenetre(){
this.setTitle("Bouton");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
//On définit le layout à utiliser sur le contentPane
this.setLayout(new BorderLayout());
//On ajoute le bouton au contentPane de la JFrame
//Au centre
this.getContentPane().add(new JButton("CENTER"), BorderLayout.CENTER);
//Au nord
this.getContentPane().add(new JButton("NORTH"), BorderLayout.NORTH);
//Au sud
this.getContentPane().add(new JButton("SOUTH"), BorderLayout.SOUTH);
//À l'ouest
this.getContentPane().add(new JButton("WEST"), BorderLayout.WEST);
//À l'est
this.getContentPane().add(new JButton("EAST"), BorderLayout.EAST);
this.setVisible(true);
}
}
|
Ce n'est pas très difficile
à comprendre. Vous définissez le
layout à utiliser
avec la méthode
setLayout() de
l'objet
JFrame ; ensuite, pour chaque composant
que vous souhaitez positionner avec
add(), vous utilisez un attribut
static de la classe
BorderLayout en deuxième paramètre.
La
liste des ces paramètres est celle citée plus haut...
Avec l'objet BorderLayout, vos composants sont soumis à
des contraintes. Pour une position NORTH
ou SOUTH, votre composant
prendra la taille nécessaire en hauteur mais prendra toute la
largeur ! Pour WEST et
EAST, celui-ci prendra la
largeur nécessaire mais toute la hauteur ! Et bien sûr pour
CENTER, tout l'espace est
utilisé !
L'objet
GridLayoutCelui-ci permet d'ajouter
des composants suivant une grille définie par un nombre de lignes et de
colonnes. Les éléments sont disposés depuis la case située en haut à gauche. Dès
qu'une ligne est remplie, on passe à la suivante.
Si nous définissons une
grille de 3 lignes et de 2 colonnes, voici ce que nous aurions :
Voici
le code de cet exemple :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 |
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Fenetre extends JFrame{
private JButton bouton = new JButton("BorderLayout.NORTH");
public Fenetre(){
this.setTitle("Bouton");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
//On définit le layout à utiliser sur le contentPane
//3 lignes sur 2 colonnes
this.setLayout(new GridLayout(3, 2));
//On ajoute le bouton au contentPane de la JFrame
this.getContentPane().add(new JButton("1"));
this.getContentPane().add(new JButton("2"));
this.getContentPane().add(new JButton("3"));
this.getContentPane().add(new JButton("4"));
this.getContentPane().add(new JButton("5"));
this.setVisible(true);
}
}
|
Ce code ne diffère pas
beaucoup du précédent. Les seules différences résident dans le fait que nous
utilisons un autre layout manager et qu'il n'y a pas besoin de définir le
positionnement lors de l'ajout du composant avec la méthode
add().
Vous devez aussi savoir que vous
pouvez définir le nombre de lignes et le nombre de colonnes avec des méthodes de
l'objet :
Code : Java
1
2
3
4
5
6 |
GridLayout gl = new GridLayout();
gl.setColumns(2);
gl.setRows(3);
this.setLayout(gl);
// ou en abrégé : GridLayout gl = new GridLayout(3, 2);
|
Vous pouvez aussi spécifier
des espaces entre les colonnes et entre les lignes.
Code :
Java
1
2
3
4
5 |
GridLayout gl = new GridLayout(3, 2);
gl.setHgap(5); // 5 pixels d'espace entre les colonnes (H comme Horizontal)
gl.setVgap(5); // 5 pixels d'espace entre les lignes (V comme Vertical)
//ou en abrégé : GridLayout gl = new GridLayout(3, 2, 5, 5);
|
Ce qui donnerait :
L'objet
FlowLayoutCelui-ci est certainement
le plus facile à utiliser ! Il ne fait que centrer les composants dans le
conteneur. Regardez plutôt :
Ah
! On dirait bien que nous venons de trouver le layout manager défini par défaut
dans les objets
JPanel.
Si vous mettez plusieurs
composants avec ce gestionnaire, dès que la place est devenu trop étroite, il
passe à la ligne inférieure. Voyez plutôt :
Il
existe encore deux types de layout managers :
- le CardLayout
- le GridBagLayout.
Je suis
dans l'incapacité de vous en parler car je ne les ai encore jamais utilisés...
Sachez seulement que le dernier cité est le plus précis, mais aussi le plus
compliqué à utiliser... Si vous êtes intéressés par ces deux gestionnaires, vous
pouvez trouver des renseignements
ici
pour le
CardLayout et
là
pour le
GridBagLayout.
Il faut que vous
sachiez que les IHM ne sont en fait qu'une imbrication de composants les uns
dans les autres, positionnés avec des layout managers. Vous allez voir tout de
suite de quoi je veux parler...
Continuons dans notre lancée
Vous avez vu comment utiliser les layout managers ;
nous allons donc continuer à jouer avec nos composants.
Vous savez :
- créer une fenêtre
- faire un conteneur personnalisé
- placer celui-ci dans votre fenêtre
- créer un bouton et le placer sur votre fenêtre.
Nous
allons maintenant utiliser notre conteneur personnalisé et un bouton.
Vous
pouvez revenir dans votre projet contenant notre animation créée dans les
chapitres précédents.
Le but est de mettre notre animation au centre de
notre fenêtre et un bouton en bas de celle-ci. Comme ceci :
Je
vous laisse réfléchir quelques minutes, vous avez tous les outils pour y arriver
!
Secret (cliquez
pour afficher)
Voilà le nouveau code de notre classe
Fenetre
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 |
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JButton bouton = new JButton("mon bouton");
private JPanel container = new JPanel();
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
container.add(bouton, BorderLayout.SOUTH);
this.setContentPane(container);
this.setVisible(true);
go();
}
private void go(){
//Les coordonnées de départ de notre rond
int x = pan.getPosX(), y = pan.getPosY();
//Le booléen pour savoir si on recule ou non sur l'axe X
boolean backX = false;
//Le booléen pour savoir si on recule ou non sur l'axe Y
boolean backY = false;
//Pour cet exemple, j'utilise une boucle while
//Vous verrez qu'elle fonctionne très bien
while(true){
//Si la coordonnée x est inférieure à 1, on avance
if(x < 1)backX = false;
//Si la coordonnée x est supérieure à la taille du panneau
//moins la taille du rond, on avance
if(x > pan.getWidth()-50)backX = true;
//idem pour l'axe Y
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
//Si on avance, on incrémente la coordonnée
if(!backX)
pan.setPosX(++x);
//Sinon, on décrémente
else
pan.setPosX(--x);
//Idem pour l'axe Y
if(!backY)
pan.setPosY(++y);
else
pan.setPosY(--y);
//On redessine notre panneau
pan.repaint();
//Comme on dit : la pause s'impose ! Ici, 3 centièmes de secondes
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
|
Assez
facile... N'est-ce pas ?
Avant de voir comment les boutons interagissent avec
l'application, je vous propose de voir comment personnaliser ceux-ci !
Une classe Bouton personnalisée
Tout comme dans le deuxième chapitre, nous allons faire
une classe : appelons-la
Bouton, héritée (dans notre cas, nous
allons faire hériter notre classe de
javax.swing.JButton), et nous allons
redéfinir la méthode
paintComponent. Vous devriez y arriver
tous seuls.
Pour cet exemple j'ai obtenu ceci :
Voici
la classe
Bouton de cette application :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 |
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JButton;
public class Bouton extends JButton {
private String name;
public Bouton(String str){
super(str);
this.name = str;
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
g2d.setPaint(gp);
g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
g2d.setColor(Color.white);
g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
}
}
|
J'ai aussi fait un bouton
personnalisé avec une image de fond (un png...) que voici :
Et
voilà le résultat :
C'est
sympa aussi !

J'ai
appliqué l'image sur l'intégralité du fond comme je l'ai montré lorsque nous
nous amusions avec notre
Panneau. Voici le code de la classe
Bouton
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 |
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
public class Bouton extends JButton{
private String name;
private Image img;
public Bouton(String str){
super(str);
this.name = str;
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
g2d.setPaint(gp);
// g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
g2d.setColor(Color.black);
g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
}
}
|
Bien sûr, ladite image est à la racine de
mon projet !
Rien de bien compliqué jusqu'à maintenant...
Et c'est à partir de là que les choses vont devenir intéressantes !

Que
diriez-vous si je vous proposais de pouvoir changer l'aspect de votre objet au
passage de la souris, lorsque vous cliquez dessus, et même lorsque vous relâchez
le clic ?
On peut faire ça ?
Bien entendu !

Il
existe des interfaces à implémenter qui permettent de gérer toutes sortes
d'événements sur votre IHM.
Le principe de fonctionnement est un peu
déroutant au premier abord, mais il est assez simple lorsqu'on a un peu
pratiqué. N'attendons pas plus et voyons ça de plus près !

Interaction avec la souris : l'interface MouseListener
Avant de nous lancer dans l'implémentation de cette
dernière, nous allons voir ce nous allons obtenir à la fin :
Vous
avez vu ce que nous allons obtenir, mais vous allez tout de même passer par un
peu de théorie avant d'arriver à ce résultat.
Je ne me suis pas encore
attardé sur le sujet (et je ne le ferai toujours pas), mais, pour arriver à
détecter les événements qui surviennent à votre composant, Java utilise ce qu'on
appelle le
design pattern Observer.
Je ne vous l'expliquerai pas dans le détail tout de suite, une partie
concernant les
design patterns sera rédigée...
En
fait, un
dp est un modèle de conception, une idée
directrice afin d'avoir des programmes stables, réutilisables à souhait et
paramétrables au possible !
Le design pattern
Observer
consiste en un modèle qui permet à des objets de se tenir au courant
automatiquement. Ceci se fait par le biais d'interfaces que les objets devant se
tenir au courant doivent implémenter.
Pour le moment, retenez qu'un objet
observable implémente une interface communément appelée
Observable et qu'un
observateur va, lui, implémenter une interface communément appelée
Observer (ceci dans le
cas où nous utilisons le pattern Observer fourni avec Java ; oui, ces interfaces
existent...). Le principe est simple :
- l'objet observable va avoir un ou plusieurs objets observateurs
- lorsqu'une donnée change dans un objet observable, il met tous ses objets
observateurs abonnés à sa "news letter" au courant
!
Ce principe, adapté aux composants
swing, permet aux objets composants d'être
écoutés et donc de pouvoir faire réagir votre application en conséquence !

Ne
nous attardons pas sur le sujet, nous y reviendrons.
Maintenant que vous
avez une très vague idée de la façon dont Java gère les événements, nous allons
commencer à gérer les passages de notre souris sur notre objet !
Comme
vous l'avez sûrement deviné, vous allez devoir implémenter l'interface
MouseListener
dans votre classe
Bouton. Utilisez l'astuce d'Eclipse vue
lors des implémentations d'interfaces, afin de générer automatiquement les
méthodes à implémenter.
Nous aurons aussi à dire à notre classe
Bouton
qu'elle va devoir tenir quelqu'un au courant de ses changements d'état par
rapport à la souris. Ce quelqu'un n'est autre... qu'elle-même !

Eh
oui... Notre classe va s'écouter elle-même, donc ; dès que notre objet
observable, notre bouton, va avoir des informations concernant les actions de la
souris, il va dire à l'objet qui l'observe, lui-même, ce qu'il doit faire !
Ceci se fait grâce à la méthode
addMouseListener(MouseListener obj) qui prend un
objet
MouseListener
en paramètre ; ici, elle prendra
this.
Rappelez-vous que vous pouvez utiliser le type d'une interface comme
super-type : ici, notre classe implémente l'interface MouseListener , nous
pouvons donc utiliser cet objet comme référence de cette interface !
Voici notre classe
Bouton à présent
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 |
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
public class Bouton extends JButton implements MouseListener{
private String name;
private Image img;
public Bouton(String str){
super(str);
this.name = str;
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Avec cette instruction, notre objet va s'écouter lui-même
//Dès qu'un événement de la souris sera intercepté, il sera au courant !
this.addMouseListener(this);
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
g2d.setPaint(gp);
// g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
g2d.setColor(Color.black);
g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
}
/**
* Méthode appelée lors du clic de souris
*/
public void mouseClicked(MouseEvent event) {
}
/**
* Méthode appelée lors du survol de la souris
*/
public void mouseEntered(MouseEvent event) {
}
/**
* Méthode appelée lorsque la souris sort de la zone du bouton
*/
public void mouseExited(MouseEvent event) {
}
/**
* Méthode appelée lorsque l'on presse le clic gauche de la souris
*/
public void mousePressed(MouseEvent event) {
}
/**
* Méthode appelée lorsque l'on relâche le clic de souris
*/
public void mouseReleased(MouseEvent event) {
}
}
|
C'est par le biais de ces
différentes méthodes que nous allons gérer les différentes images à dessiner
dans notre objet ! Mais rappelez-vous que :
Même si vous n'utilisez pas
toutes les méthodes d'une interface, vous DEVEZ tout de même mettre le squelette
des méthodes non utilisées (avec les accolades), et ceci est aussi valable pour
les classes abstraites.
Dans notre cas, la méthode repaint() est appelée de façon tacite.
Lorsqu'un événement est déclenché, notre objet se redessine
automatiquement ! Comme lorsque vous redimensionnez votre fenêtre dans
les premiers chapitres...
Avec cette information, nous n'avons plus
qu'à changer notre image selon la méthode invoquée :
- notre objet aura une teinte jaune lors du survol de la souris
- une teinte orangée lorsque l'on pressera le clic gauche
- reviendra à la normale si on relâche le clic.
Pour ce faire,
voici les fichier
.png dont je me suis servi (mais rien
ne vous empêche de les faire vous-mêmes

).
Je vous rappelle que dans le code qui suit,
les fichiers images sont mis à la racine du projet !
!
Maintenant, voici le code de notre classe
Bouton personnalisée
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108 |
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
public class Bouton extends JButton implements MouseListener{
private String name;
private Image img;
public Bouton(String str){
super(str);
this.name = str;
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.addMouseListener(this);
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
g2d.setPaint(gp);
// g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
g2d.setColor(Color.black);
g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
}
@Override
public void mouseClicked(MouseEvent event) {
//Pas utile d'utiliser cette méthode ici
}
@Override
public void mouseEntered(MouseEvent event) {
//Nous changeons le fond en jaune pour notre image lors du survol
//avec le fichier fondBoutonHover.png
try {
img = ImageIO.read(new File("fondBoutonHover.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mouseExited(MouseEvent event) {
//Nous changeons le fond en vert pour notre image lorsqu'on quitte le bouton
//avec le fichier fondBouton.png
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mousePressed(MouseEvent event) {
//Nous changeons le fond en orangé pour notre image lors du clic gauche
//avec le fichier fondBoutonClic.png
try {
img = ImageIO.read(new File("fondBoutonClic.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mouseReleased(MouseEvent event) {
//Nous changeons le fond en orangé pour notre image
//lorsqu'on relâche le clic
//avec le fichier fondBoutonHover.png
try {
img = ImageIO.read(new File("fondBoutonHover.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
|
Et voilà le travail ! Il va
de soi que si vous avez fait un
clic-droit /
Enregistrer-sous sur mes images, elles ne doivent pas avoir le même nom
que sur mon morceau de code... Vous
DEVEZ mettre le nom que
vous leur avez donné... Je sais ça va de soi... Mais on ne sait jamais
!
Et maintenant, vous avez un bouton personnalisé qui réagit aux passages
de souris !

Mais
un bémol se profile à l'horizon...
Lequel ? L'objet marche très bien !
Je sais
qu'il va y avoir des p'tits malins qui vont cliquer sur le bouton et relâcher le
clic en dehors du bouton !

Dans
ce cas, vous devez voir que le fond du bouton est jaune, vu que c'est ce que
nous avons demandé de faire à notre méthode
mouseReleased ! Pour palier à ce problème, nous
allons vérifier que, lorsque le clic est relâché, la souris est toujours sur le
bouton.
Comment tu vas réussir à faire ça ? J'ai eu beau
regarder dans les méthodes proposées par notre objet, mais il n'y a rien qui
nous permette de faire ça !
Je sais... Mais vous n'avez pas regardé au bon endroit !
Maintenant que nous avons implémenté l'interface
MouseListener, il y a
un autre objet que nous n'avons pas encore utilisé... Vous ne le voyez pas ?
C'est le paramètre présent dans toutes les méthodes de cette interface ! Oui,
c'est
MouseEvent.
Grâce à cet objet, nous
pouvons avoir beaucoup de renseignements sur les événements. Nous ne
détaillerons pas tout ici mais vous verrez quelques utilités de ces types
d'objets tout au long de cette partie !
Dans notre cas, nous pouvons
récupérer la position X et Y de notre souris par rapport à notre
Bouton, tout ceci avec
les méthodes
getX() et
getY(). Donc, si nous relâchons
notre clic de souris en dehors de la zone où se trouve notre objet, la valeur
retournée par la méthode
getY()
sera négative !
Voici enfin le code final de notre classe
Bouton :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 |
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
public class Bouton extends JButton implements MouseListener{
private String name;
private Image img;
public Bouton(String str){
super(str);
this.name = str;
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.addMouseListener(this);
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
g2d.setPaint(gp);
// g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
g2d.setColor(Color.black);
g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
}
@Override
public void mouseClicked(MouseEvent event) {
//Pas utile d'utiliser cette méthode ici
}
@Override
public void mouseEntered(MouseEvent event) {
//Nous changeons le fond en jaune pour notre image lors du survol
//avec le fichier fondBoutonHover.png
try {
img = ImageIO.read(new File("fondBoutonHover.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mouseExited(MouseEvent event) {
//Nous changeons le fond en vert pour notre image lorsqu'on quitte le bouton
//avec le fichier fondBouton.png
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mousePressed(MouseEvent event) {
//Nous changeons le fond en orangé pour notre image lors du clic gauche
//avec le fichier fondBoutonClic.png
try {
img = ImageIO.read(new File("fondBoutonClic.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mouseReleased(MouseEvent event) {
//Nous changeons le fond en orangé pour notre image
//lorsqu'on relâche le clic
//avec le fichier fondBoutonHover.png
//Si on est à l'extérieur de l'objet, on dessine le fond par défaut
if(event.getY() > 0)
{
try {
img = ImageIO.read(new File("fondBoutonHover.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//Sinon on met le fond jaune, la souris est encore dessus...
else
{
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
|
Vous allez voir tout au long des chapitres qui
suivent qu'il y a différentes interfaces pour les différentes actions possibles
sur une IHM ! Sachez seulement qu'il y a une convention aussi pour ces
interfaces.
Leur nom commence par le type d'action ensuite suivi du mot
Listener. Nous avons vu ici les
actions de la souris, et voyez le nom de l'interface : MouseListener
Voilà ! Ce chapitre
est clos !
Eh ! Attends... Notre bouton ne sais toujours rien
faire !
C'est justement le sujet du prochain chapitre. Passez faire un
tour sur le topo de cette partie et si vous avez un score raisonnable au QCM,
vous pourrez y aller...

Ce qu'il faut retenir
- Un bouton s'utilise avec la classe JButton présente dans le package
javax.swing.
- Pour ajouter un bouton à une fenêtre, vous devez utiliser la méthode add() de son contentPane.
- Il existe des objets afin de pouvoir positionner nos composants sur un
contentPane ou un conteneur : les layout
managers.
- Les layout managers se trouvent dans le package java.awt.
- Le layout manager par défaut d'un objet JPanel est le FlowLayout.
- Outre le FlowLayout, vous avez aussi le
BorderLayout, le GridLayout, le
CardLayout et le GridBagLayout. Il en
existe sûrement d'autres sur le net... mais je ne les connais pas...
- On définit un layout sur un conteneur grâce à la méthode setLayout().
- Vous pouvez interagir sur un
composant avec votre souris en implémentant l'interface
MouseListener.
- Lorsque vous implémentez une interface <...>Listener, vous dites à votre
classe qu'elle doit se préparer à observer des événements du type de
l'interface... Vous devez donc spécifier QUI doit observer et QUE
doit-elle observer, avec les méthodes du type add<...>Listener(<...>Listener).
Toujours motivés pour continuer ?
Je ne vous ai pas
trop fait peur ?
Très bien, alors voyons : Interaction bouton(s) - application.
Interaction bouton(s) - application
Nous y voilà !
Dans ce chapitre, votre objet
Bouton pourra enfin communiquer avec votre application
!
Je pense tout de même que le chapitre précédent à dû vous
plaire...
Nous allons voir comment faire, mais aussi qu'il y a plusieurs
façons de faire...
Ensuite, il ne tiendra qu'à vous de bien choisir...
En avant, moussaillons.

Déclencher une action : l'interface ActionListener
Tout est dans le titre !

Afin
de gérer les différentes actions à effectuer selon le bouton sur lequel on
clique, nous allons utiliser l'interface
ActionListener.
Cependant, nous
n'allons pas implémenter cette dernière dans notre classe
Bouton, mais dans notre
classe
Fenetre... Le but étant de faire en sorte
que lorsque nous cliquons sur notre bouton, il se passe quelque chose dans notre
application comme changer un état, une variable, faire une incrémentation...
Enfin n'importe quelle action !
Comme je vous l'ai expliqué dans la
partie précédente, lorsque nous faisons
addMouseListener, nous prévenons l'objet
observé qu'un objet doit être mis au courant ! Ici, nous voulons que ce soit
notre application, notre
Fenetre, qui écoute notre
Bouton, le but final
étant de pouvoir lancer ou d'arrêter l'animation de notre
Panneau.
Avant
d'en arriver là, nous allons faire plus simple. Nous allons voir dans un premier
temps l'implémentation de l'interface
ActionListener. Afin de vous montrer toute
la puissance de cette interface, nous allons utiliser un nouvel objet présent
dans le package
javax.swing : le
JLabel.
Cet objet
est en fait comme une étiquette, il est spécialisé dans l'affichage de texte ou
d'image... Il est donc parfait pour notre premier exemple !
Pour
l'instanciation ou l'initialisation, il fonctionne un peu comme le
JButton, voyez plutôt
:
Code : Java
1
2
3
4 |
JLabel label1 = new JLabel();
label1.setText("mon premier JLabel");
//Ou encore
JLabel label2 = new JLabel("Mon deuxième JLabel");
|
Créez une variable
d'instance de type
JLabel - appelons-la
label - initialisez-la avec le texte qui vous
plaît, puis ajoutez-la avec votre
contentPane en
BorderLayout.NORTH.
Voici
le résultat :
Et
le code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 |
public class Fenetre extends JFrame {
private Panneau pan = new Panneau();
private Bouton bouton = new Bouton("mon bouton");
private JPanel container = new JPanel();
private JLabel label = new JLabel("Le JLabel");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
container.add(bouton, BorderLayout.SOUTH);
container.add(label, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
go();
}
//...
}
|
Vous pouvez voir le texte en
haut à gauche... L'alignement par défaut de cet objet est à gauche mais vous
pouvez changer quelques paramètres, comme :
- l'alignement
- la police à utiliser
- la couleur de police
- ...
Voilà un code qui met tout ceci en pratique, et son
aperçu.
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 |
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
container.add(bouton, BorderLayout.SOUTH);
//Définition d'une police d'écriture
Font police = new Font("Tahoma", Font.BOLD, 16 );
//On applique celle-ci aux JLabel
label.setFont(police);
//On change la couleur de police
label.setForeground(Color.blue);
//Et on change l'alignement du texte grâce aux attributs static de la classe JLabel
label.setHorizontalAlignment(JLabel.CENTER);
container.add(label, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
go();
}
|
Aperçu :
Maintenant
que notre étiquette est exactement comme nous le voulons, nous allons pouvoir
implémenter l'interface
ActionListener.
Lorsque vous avez
implémenté les méthodes de l'interface, vous vous apercevez que celle-ci n'en
contient qu'une seule !
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame implements ActionListener{
private Panneau pan = new Panneau();
private Bouton bouton = new Bouton("mon bouton");
private JPanel container = new JPanel();
private JLabel label = new JLabel("Le JLabel");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
container.add(bouton, BorderLayout.SOUTH);
//Définition d'une police d'écriture
Font police = new Font("Tahoma", Font.BOLD, 16 );
//On applique celle-ci aux JLabel
label.setFont(police);
//On change la couleur de police
label.setForeground(Color.blue);
//Et on change l'alignement du texte grâce aux attributs static de la classe JLabel
label.setHorizontalAlignment(JLabel.CENTER);
container.add(label, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
go();
}
//...
//*******************************************************************************
// LA VOILAAAAAAAAAAAAAA
//*******************************************************************************
/**
* C'est la méthode qui sera appelée lors d'un clic sur notre bouton
*/
public void actionPerformed(ActionEvent arg0) {
}
}
|
Nous allons maintenant
prévenir notre objet
Bouton que notre objet
Fenetre l'écoute ! Vous
l'avez deviné, nous ajoutons notre objet
Fenetre à la liste des objets qui écoutent
notre
Bouton grâce à la méthode
addActionListener(ActionListener obj) invoquée
sur la variable
bouton. Ajoutez
cette instruction dans le constructeur, en passant
this en paramètre
(c'est notre
Fenetre qui écoute notre
bouton...).
Une fois ceci fait, nous allons changer le texte de notre
JLabel
dans la méthode
actionPerformed ;
en fait, nous allons compter combien de fois on clique sur notre bouton... Pour
cela, nous ajoutons une variable d'instance de type
int dans notre classe :
appelons-la
compteur. Dans la
méthode
actionPerformed, nous
allons incrémenter ce compteur et afficher son contenu dans notre
étiquette.
Voici le code de notre objet mis à jour :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame implements ActionListener{
private Panneau pan = new Panneau();
private Bouton bouton = new Bouton("mon bouton");
private JPanel container = new JPanel();
private JLabel label = new JLabel("Le JLabel");
/**
* Compteur de clics !
*/
private int compteur = 0;
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
//On ajoute notre Fenetre à la liste des auditeurs de notre Bouton
bouton.addActionListener(this);
container.add(bouton, BorderLayout.SOUTH);
//Définition d'une police d'écriture
Font police = new Font("Tahoma", Font.BOLD, 16 );
//On applique celle-ci aux JLabel
label.setFont(police);
//On change la couleur de police
label.setForeground(Color.blue);
//Et on change l'alignement du texte grâce aux attributs static de la classe JLabel
label.setHorizontalAlignment(JLabel.CENTER);
container.add(label, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
go();
}
private void go(){
//Les coordonnées de départ de notre rond
int x = pan.getPosX(), y = pan.getPosY();
//Le booléen pour savoir si on recule ou non sur l'axe X
boolean backX = false;
//Le booléen pour savoir si on recule ou non sur l'axe Y
boolean backY = false;
//Pour cet exemple, j'utilise une boucle while
//Vous verrez qu'elle fonctionne très bien
while(true){
//Si la coordonnée x est inférieure à 1, on avance
if(x < 1)backX = false;
//Si la coordonnée x est supérieure à la taille du Panneau
//moins la taille du rond on avance
if(x > pan.getWidth()-50)backX = true;
//idem pour l'axe Y
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
//Si on avance, on incrémente la coordonnée
if(!backX)
pan.setPosX(++x);
//Sinon on décrémente
else
pan.setPosX(--x);
//Idem pour l'axe Y
if(!backY)
pan.setPosY(++y);
else
pan.setPosY(--y);
//On redessine notre Panneau
pan.repaint();
//Comme on dit : la pause s'impose ! Ici, 3 centièmes de secondes
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//*******************************************************************************
// LA VOILAAAAAAAAAAAAAA
//*******************************************************************************
/**
* C'est la méthode qui sera appelée lors d'un clic sur notre bouton
*/
public void actionPerformed(ActionEvent arg0) {
//Lorsque nous cliquons sur notre bouton, on met à jour le JLabel
this.compteur++;
label.setText("Vous avez cliqué " + this.compteur + " fois");
}
}
|
Et le résultat :
On
commence à faire du sérieux, là ! !

Mais
attendez, on ne fait que commencer... Eh oui ! Nous allons maintenant ajouter un
deuxième bouton à notre
Fenetre, à côté de notre premier bouton
(vous êtes libres d'utiliser la classe personnalisée ou un
JButton) !
Personnellement, je vais utiliser des boutons normaux maintenant ; en effet, la
façon dont on écrit le nom de notre bouton, dans notre classe personnalisée,
n'est pas assez souple et donc l'affichage peut être décevant...

Bref,
nous avons maintenant deux boutons écoutés par notre objet
Fenetre.
Vous devez créer un deuxième JPanel qui va contenir nos deux boutons et
insérer celui-ci dans le contentPane en
BorderLayout.SOUTH.
Si
vous tentez de mettre deux composants au même endroit avec un BorderLayout, seul le dernier composant ajouté apparaîtra
! Eh oui, le composant prend toute la place dans un BorderLayout !
Voilà notre nouveau
code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame implements ActionListener{
private Panneau pan = new Panneau();
private JButton bouton = new JButton("bouton 1");
private JButton bouton2 = new JButton("bouton 2");
private JPanel container = new JPanel();
private JLabel label = new JLabel("Le JLabel");
/**
* Compteur de clics !
*/
private int compteur = 0;
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
//On ajoute notre Fenetre à la liste des auditeurs de notre Bouton
bouton.addActionListener(this);
bouton2.addActionListener(this);
JPanel south = new JPanel();
south.add(bouton);
south.add(bouton2);
container.add(south, BorderLayout.SOUTH);
//Définition d'une police d'écriture
Font police = new Font("Tahoma", Font.BOLD, 16 );
//On applique celle-ci aux JLabel
label.setFont(police);
//On change la couleur de police
label.setForeground(Color.blue);
//Et on change l'alignement du texte grâce aux attributs static de la classe JLabel
label.setHorizontalAlignment(JLabel.CENTER);
container.add(label, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
go();
}
//...
//*******************************************************************************
// LA VOILAAAAAAAAAAAAAA
//*******************************************************************************
/**
* C'est la méthode qui sera appelée lors d'un clic sur notre bouton
*/
public void actionPerformed(ActionEvent arg0) {
//Lorsque nous cliquons sur notre bouton, on met à jour le JLabel
this.compteur++;
label.setText("Vous avez cliqué " + this.compteur + " fois");
}
}
|
Et le résultat :
Le
problème maintenant est :
comment faire faire deux choses différentes dans la
méthode actionPerformed
?
En effet ! Si nous laissons la méthode
actionPerformed telle qu'elle est, les deux
boutons auront la même action lorsque nous cliquerons dessus. Essayez, et vous
verrez !
Il existe un moyen de savoir qui a déclenché l'événement, en
utilisant l'objet passé en paramètre dans la méthode
actionPerformed. Nous allons utiliser la méthode
getSource() de cet objet pour
connaître le nom de l'instance qui a généré l'événement.
Testez la méthode
actionPerformed suivante, et voyez
le résultat :
Code : Java
1
2
3
4
5
6
7
8 |
public void actionPerformed(ActionEvent arg0) {
if(arg0.getSource() == bouton)
label.setText("Vous avez cliqué sur le bouton 1");
if(arg0.getSource() == bouton2)
label.setText("Vous avez cliqué sur le bouton 2");
}
|
Résultat :
Vous
pouvez constater que notre code fonctionne très bien ! Mais cette approche n'est
pas très orientée objet... Si vous avez une multitude de boutons sur votre
IHM... vous allez avoir une méthode
actionPerformed très chargée !
Nous pourrions
créer deux objets à part, chacun écoutant un bouton, dont le rôle serait de
faire un traitement précis par bouton... Cependant, si dans nos traitements nous
avons besoin de modifier des données internes à la classe contenant nos boutons,
il faudrait passer ces données (ou objets) à cet objet... Pas terrible non
plus.
On commence à te connaître, maintenant ! Tu as une
idée derrière la tête...
Je suis démasqué !
Il existe en Java un type de classe particulière. Voyons ça tout de suite
!
Parlez avec votre classe intérieure
En Java, vous pouvez faire ce qu'on appelle des
classes internes.
Ceci
consiste à déclarer une classe dans une classe ! Je sais, ça paraît
tordu mais vous allez voir que c'est très pratique.
En effet, ces
classes possèdent tous les avantages des classes normales, héritant d'une
super-classe ou implémentant une interface, elles bénéficieront donc des
bénéfices du polymorphisme et de la covariance des variables !

En
plus, elles ont l'avantage d'avoir accès aux attributs de la classe dans
laquelle elle est déclarée !
Dans le cas qui nous intéresse, ceci permet
de faire une implémentation de l'interface
ActionListener, détachée de notre classe
Fenetre,
mais pouvant utiliser ses attributs !
La déclaration d'une telle classe se
fait exactement comme une classe normale, sauf qu'elle est dans une autre
classe... Ce qui donne ceci :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13 |
public class MaClasseExterne{
public MaClasseExterne(){
//....
}
class MaClassInterne{
public MaClassInterne(){
//...
}
}
}
|
Grâce à ceci, nous allons
pouvoir faire une classe spécialisée dans l'écoute de composants et qui a un
travail précis à faire ! Dans notre exemple, nous allons juste faire deux
classes internes implémentant chacune l'interface
ActionListener ; elles
redéfiniront donc la méthode
actionPerformed :
- BoutonListener
: qui écoutera le premier bouton
- Bouton2Listener
: qui écoutera le second !
Une fois que
ceci est fait, il ne nous reste plus qu'à dire à chaque bouton : "
qui l'écoute", avec la méthode
addActionListener.
Voici la classe
Fenetre mise à jour :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JButton bouton = new JButton("bouton 1");
private JButton bouton2 = new JButton("bouton 2");
private JPanel container = new JPanel();
private JLabel label = new JLabel("Le JLabel");
private int compteur = 0;
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
//Ce sont maintenant nos classes internes qui écoutent nos boutons
bouton.addActionListener(new BoutonListener());
bouton2.addActionListener(new Bouton2Listener());
JPanel south = new JPanel();
south.add(bouton);
south.add(bouton2);
container.add(south, BorderLayout.SOUTH);
Font police = new Font("Tahoma", Font.BOLD, 16 );
label.setFont(police);
label.setForeground(Color.blue);
label.setHorizontalAlignment(JLabel.CENTER);
container.add(label, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
go();
}
private void go(){
//Les coordonnées de départ de notre rond
int x = pan.getPosX(), y = pan.getPosY();
//Le booléen pour savoir si on recule ou non sur l'axe X
boolean backX = false;
//Le booléen pour savoir si on recule ou non sur l'axe Y
boolean backY = false;
//Pour cet exemple, j'utilise une boucle while
//Vous verrez qu'elle fonctionne très bien
while(true){
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* classe qui écoute notre bouton
*/
class BoutonListener implements ActionListener{
/**
* Redéfinition de la méthode actionPerformed
*/
public void actionPerformed(ActionEvent arg0) {
label.setText("Vous avez cliqué sur le bouton 1");
}
}
/**
* classe qui écoute notre bouton2
*/
class Bouton2Listener implements ActionListener{
/**
* Redéfinition de la méthode actionPerformed
*/
public void actionPerformed(ActionEvent e) {
label.setText("Vous avez cliqué sur le bouton 2");
}
}
}
|
Et le résultat est parfait :
Vous pouvez même constater que nos classes internes
ont accès aux attributs déclarés private dans notre classe Fenetre !
Nous n'avons plus à nous
soucier de qui a déclenché l'événement maintenant, car nous avons une classe qui
écoute chaque bouton ! Nous pouvons souffler un peu, une grosse épine vient de
nous être retirée du pied.
Vous le savez, mais vous pouvez faire écouter votre
bouton par plusieurs classes... Il vous suffit d'ajouter les classes qui
écoutent le boutons avec addActionListener.
Eh oui, faites le
test...
Créez une troisième classe interne, peu importe son nom (moi, je
l'appelle
Bouton3Listener), implémentez l'interface
ActionListener dans celle-ci et
contentez-vous de faire un simple
System.out.println dans la méthode
actionPerformed. N'oubliez pas d'ajouter cette
dernière à la liste des classes qui écoutent votre bouton (n'importe lequel des
deux... Moi, j'ai choisi le premier).
Je ne vous donne que le code
ajouté :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 |
//Les imports
public class Fenetre extends JFrame{
//Les variables d'instance...
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
//Première classe écoutant mon bouton
bouton.addActionListener(new BoutonListener());
//Deuxième classe écoutant mon bouton
bouton.addActionListener(new Bouton3Listener());
bouton2.addActionListener(new Bouton2Listener());
JPanel south = new JPanel();
south.add(bouton);
south.add(bouton2);
container.add(south, BorderLayout.SOUTH);
Font police = new Font("Tahoma", Font.BOLD, 16 );
label.setFont(police);
label.setForeground(Color.blue);
label.setHorizontalAlignment(JLabel.CENTER);
container.add(label, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
go();
}
//...
class Bouton3Listener implements ActionListener{
/**
* Redéfinition de la méthode actionPerformed
*/
public void actionPerformed(ActionEvent e) {
System.out.println("Ma classe interne numéro 3 écoute bien !");
}
}
}
|
Et le résultat :
Les
classes internes sont vraiment des classes à part entière ! Elles peuvent aussi
être héritées d'une super-classe... De ce fait, c'est presque comme si nous
avions de l'héritage multiple, ça n'en est pas... Mais ça y ressemble. Donc, ce
code est valide :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 |
public class MaClasseExterne extends JFrame{
public MaClasseExterne(){
//....
}
class MaClassInterne extends JPanel{
public MaClassInterne(){
//...
}
}
class MaClassInterne2 extends JButton{
public MaClassInterne(){
//...
}
}
}
|
Vous
voyez bien que ce genre de classes peuvent s'avérer très utile...

Bon.
Nous avons réglé le problème d'implémentation, nous avons deux boutons qui sont
écoutés, il ne nous reste plus qu'à lancer et arrêter notre animation avec
ceux-ci !
Contrôler votre animation : lancement et arrêt
Nous attaquons la dernière ligne droite de ce chapitre.
Donc, pour réussir à gérer le lancement et l'arrêt de notre animation, nous
allons devoir modifier un peu le code de notre classe
Fenetre.
Il va
falloir changer le libellé de ceux-ci, le premier affichera
Go et le deuxième
Stop et, pour
éviter d'arrêter l'animation alors que celle-ci n'est pas lancée et ne pas
l'animer alors qu'elle l'est déjà, nous allons tantôt activer / désactiver les
boutons. Je m'explique.
- Au lancement, l'animation est lancée. Le bouton Go ne sera pas cliquable alors que Stop, oui.
- Si l'animation est stoppée, le bouton Stop ne
sera plus cliquable et le bouton Go le sera.
Tu ne nous as pas dit comment on fait ça !
Je sais... Mais je vous cache encore pas mal de choses...
Ne vous
inquiétez pas, c'est très simple à faire ; il existe une méthode pour faire ça
:
Code : Java
1
2
3 |
JButton bouton = new JButton("bouton");
bouton.setEnabled(false); // Votre bouton n'est plus cliquable !
bouton.setEnabled(true); // Votre bouton de nouveau cliquable !
|
J'ajouterais que vous pouvez
faire pas mal de choses avec ces objets ! Soyez curieux et testez les méthodes
de ces objets, allez faire un tour sur le site de Sun Microsystems, fouillez,
fouinez...
L'une des méthodes qui s'avère souvent utile et qui est utilisable
pour tous ces objets (et les objets que nous verrons plus tard) est la méthode
de gestion de dimension. Il ne s'agit pas de la méthode
setSize(), mais la méthode
setPreferredSize(int width, int height). Cette
méthode prend un objet
Dimension en paramètre qui lui, prend
deux entiers en paramètre.
Voilà un exemple :
Code : Java
1 |
bouton.setPreferredSize(new Dimension(150, 120));
|
Ce qui donne sur notre
application :
Retournons
à nos moutons...

Afin
de bien gérer notre animation, il va falloir améliorer notre méthode
go().
Nous allons sortir de
cette méthode les deux entiers dont nous nous servions afin de recalculer les
coordonnées de notre rond et, concernant la boucle infinie, elle doit dorénavant
pouvoir être stoppée !
Pour réussir ceci, nous allons déclarer une variable
d'instance de type booléen qui changera d'état selon le bouton sur lequel nous
cliquerons, et nous utiliserons celui-ci comme paramètre de notre
boucle.
Voici le code de notre classe
Fenetre :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JButton bouton = new JButton("Go");
private JButton bouton2 = new JButton("Stop");
private JPanel container = new JPanel();
private JLabel label = new JLabel("Le JLabel");
private int compteur = 0;
private boolean animated = true;
private boolean backX, backY;
private int x,y ;
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
//Ce sont maintenant nos classes internes qui écoutent nos boutons
bouton.addActionListener(new BoutonListener());
bouton.setEnabled(false);
bouton2.addActionListener(new Bouton2Listener());
JPanel south = new JPanel();
south.add(bouton);
south.add(bouton2);
container.add(south, BorderLayout.SOUTH);
Font police = new Font("Tahoma", Font.BOLD, 16 );
label.setFont(police);
label.setForeground(Color.blue);
label.setHorizontalAlignment(JLabel.CENTER);
container.add(label, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
go();
}
private void go(){
//Les coordonnées de départ de notre rond
x = pan.getPosX();
y = pan.getPosY();
//Pour cet exemple, j'utilise une boucle while
//Vous verrez qu'elle fonctionne très bien
while(this.animated){
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* classe qui écoute notre bouton
*/
class BoutonListener implements ActionListener{
/**
* Redéfinitions de la méthode actionPerformed
*/
public void actionPerformed(ActionEvent arg0) {
animated = true;
bouton.setEnabled(false);
bouton2.setEnabled(true);
go();
}
}
/**
* classe qui écoute notre bouton2
*/
class Bouton2Listener implements ActionListener{
/**
* Redéfinitions de la méthode actionPerformed
*/
public void actionPerformed(ActionEvent e) {
animated = false;
bouton.setEnabled(true);
bouton2.setEnabled(false);
}
}
}
|
À l'exécution, vous pouvez
voir :
- que le bouton Go n'est pas cliquable mais que
l'autre l'est
- que l'animation se lance
- lorsque vous cliquez sur Stop, que l'animation
s'arrête
- que le bouton Go devient cliquable
- et lors de votre tentative, que l'animation ne se lance pas !
Comment ça se fait ?
Comme je vous l'ai dit
dans le chapitre sur les conteneurs, notre application, au démarrage de
celle-ci, lance un
thread :
le
processus principal de votre programme !Au démarrage, l'animation
est lancée, celle-ci dans le même
thread que notre
objet
Fenetre.
Lorsque nous lui demandons de
s'arrêter, aucun souci : les ressources mémoire sont libérées, on sort de la
boucle infinie et l'application continue son court !
Mais lorsque nous
redemandons à l'animation de se lancer... L'instruction dans la méthode
actionPerformed appelle la méthode
go et, vu que nous sommes dans une
boucle infinie :
on reste dans la méthode go et on ne sort plus de la méthode actionPerformed de notre bouton !
Voici l'explication de ce
phénomène
Java gère les appels aux méthodes selon ce
qu'on appelle vulgairement :
La
pile !
Pour vous expliquer ceci, nous allons
prendre un exemple tout bête ; regardez cet objet :
Code
: Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 |
public class TestPile {
public TestPile(){
System.out.println("Début constructeur");
methode1();
System.out.println("Fin constructeur");
}
public void methode1(){
System.out.println("Début méthode 1");
methode2();
System.out.println("Fin méthode 1");
}
public void methode2(){
System.out.println("Début méthode 2");
methode3();
System.out.println("Fin méthode 2");
}
public void methode3(){
System.out.println("Début méthode 3");
System.out.println("Fin méthode 3");
}
}
|
Si vous instanciez cet
objet, vous obtiendrez dans votre console ceci :
Je
suppose que vous avez remarqué, avec stupéfaction, que l'ordre des instructions
est un peu bizarre !
Voici ce qu'il s'est passé :
- à l'instanciation, notre objet appelle la méthode1 ;
- cette dernière invoque la méthode2 ;
- celle-ci utilise la méthode3
et une fois terminée, la JVM retourne dans la méthode2 ;
- celle-ci terminée, on remonte à la fin de la méthode1... jusqu'à la dernière instruction
appelante : le contructeur.
Lors de tous les appels, on dit que la JVM
empile les invocations sur la pile ! Et, une fois la
dernière méthode empilée terminée, la JVM dépile celle-ci !
Voilà un
schéma résumant la situation :
Dans
notre programme, imaginez que la méthode
actionPerformed est représentée, dans le schéma
ci-dessus, par
méthode2 et que
notre méthode
go soit représentée
par
méthode3. Lorsque nous entrons
dans
méthode3, nous entrons dans
une boucle infinie ! La conséquence directe : on ne ressort jamais de cette
méthode et la JVM ne
dépile plus
!
Comment faire, alors ?
Nous allons utiliser
une nouveau
thread ! En gros, grâce à ceci, la
méthode
go se trouvera dans une
pile à part !
Attends... On arrive pourtant à arrêter l'animation
alors qu'elle est dans une boucle infinie ? Pourquoi ?
Tout simplement
parce que nous demandons de faire une bête initialisation de variable dans la
gestion de notre événement ! Si vous faites une deuxième méthode comprenant une
boucle infinie et que vous l'invoquez lors du clic sur
Stop, vous aurez exactement le même problème !
Je ne
vais pas m'éterniser là-dessus tout de suite... Nous allons voir ça au prochain
chapitre !

Mais
avant, je pense qu'un TP serait le bienvenu... Histoire de réviser un peu
!
Vous êtes d'accord ? Alors, direction le topo, le QCM, et en avant pour un
TP !

Cadeau : votre bouton personnalisé optimisé !
Vu que je vous ai fait un peu saliver pour rien... je
vous donne le code java d'un bouton corrigeant le problème d'écriture du
libellé. Je ne ferai pas trop de commentaire à son sujet, tout est dans le
code...
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 |
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
public class Bouton extends JButton implements MouseListener{
private String name;
private Image img;
public Bouton(String str){
super(str);
this.name = str;
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.addMouseListener(this);
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
g2d.setPaint(gp);
// g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
g2d.setColor(Color.black);
//Objet qui permet de connaître les propriétés d'une police, dont la taille !
FontMetrics fm = g2d.getFontMetrics();
//Hauteur de la police d'écriture
int height = fm.getHeight();
//Largeur totale de la chaîne passée en paramètre
int width = fm.stringWidth(this.name);
//On calcule donc la position du texte dans le bouton, et le tour est joué !
g2d.drawString(this.name, this.getWidth() / 2 - (width / 2), (this.getHeight() / 2) + (height / 4));
}
@Override
public void mouseClicked(MouseEvent event) {
//Pas utile d'utiliser cette méthode ici
}
@Override
public void mouseEntered(MouseEvent event) {
//Nous changeons le fond en jaune pour notre image lors du survol
//avec le fichier fondBoutonHover.png
try {
img = ImageIO.read(new File("fondBoutonHover.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mouseExited(MouseEvent event) {
//Nous changeons le fond en vert pour notre image lorsqu'on quitte le bouton
//avec le fichier fondBouton.png
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mousePressed(MouseEvent event) {
//Nous changeons le fond en orangé pour notre image lors du clic gauche
//avec le fichier fondBoutonClic.png
try {
img = ImageIO.read(new File("fondBoutonClic.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mouseReleased(MouseEvent event) {
//Nous changeons le fond en orangé pour notre image
//lorsqu'on relâche le clic
//avec le fichier fondBoutonHover.png
//Si on est à l'extérieur de l'objet, on dessine le fond par défaut
if(event.getY() > 0)
{
try {
img = ImageIO.read(new File("fondBoutonHover.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//Sinon on met le fond jaune, la souris est encore dessus...
else
{
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
|
Essayez et vous verrez !
Ce qu'il faut retenir
- L'interface utilisée dans ce chapitre est : ActionListener.
Celle-ci est présente dans le package java.awt.
- La méthode de cette interface à implémenter est actionPerformed.
- Lorsque vous avez plusieurs composants pouvant être écoutés, préférez
faire un objet écouteur par composants.
- Vous pouvez, et c'est conseillé, utiliser des classes
internes pour écouter vos composants.
- Une classe interne est une classe à l'intérieur d'une classe.
- Une telle classe a accès à toutes les données et méthodes de sa classe
externe.
- Ce type de classe peut être déclarée private.
- La JVM traite les méthodes appelées par une pile de méthode, définissant ainsi l'ordre
d'exécution de celles-ci.
- Une méthode est empilée à l'invocation de celle-ci, mais n'est
dépilée que lorsque toutes ses intructions sont
terminées !
- Vous connaissez la méthode permettant d'écrire dans un JLabel, vous pouvez
aussi récupérer le texte d'un tel objet en utilisant la méthode getText().
Une classe interne privée ?
Oui, parce que
vous pouvez créer une instance de la classe interne à l'extérieur de sa classe
externe, comme ceci :
Code : Java
1
2 |
Fenetre.BoutonListener boutonL;
boutonL = new Fenetre().new BoutonListener();
|
Par contre, vous ne pouvez
pas déclarer de variable
static
dans une classe interne... Mais une classe interne peut
être déclarée
static.
Encore un chapitre de bouclé !
Vous tenez toujours
bon ?
À tout de suite pour votre premier TP en événementiel !
TP : une calculatrice
Ah ! Ça faisait longtemps... un petit TP...

Dans
celui-ci, nous allons - enfin vous allez - pouvoir réviser tout ce que vous avez
appris dans cette partie !
- Fenetre
- Conteneur
- Bouton
- ActionPerformed
- Classe interne
- Exception. Bon, ça, c'était dans celle d'avant.
Allez mes ZérOs, en avant !
Élaboration
Bon, nous allons voir ce que doit faire notre
calculatrice.
- Un calcul simple : 12 + 3 =
- Des calculs à la chaîne... Exemple : 1 + 2 +, lorsque vous cliquez à
nouveau sur un opérateur, vous affichez le résultat du calcul précédent !
- Donner la possibilté de faire un reset, donc de tout faire recommencer à
zéro.
- Gérer l'exception pour une division par 0 !
Conception
Voilà ce que vous devriez avoir :
Maintenant,
voyons ce dont nous avons besoin pour réussir ça.
- Autant de boutons qu'il en faut.
- Autant de conteneurs que nécessaire.
- Un JLabel pour l'affichage.
- Un booléen pour savoir si un opérateur a été choisi.
- Un booléen pour savoir si nous devons effacer ce qu'il y a à l'écran et
écrire un nouveau nombre.
- Nous allons utiliser une variable de type double pour nos calculs.
- Il va falloir des classes internes qui implémenteront l'interface
ActionListener.
- Et c'est à peu près tout...
Pour alléger le nombre de classes internes, vous
pouvez en faire une qui va se charger d'écrire ce qui doit être affiché à
l'écran.
Utiliser la méthode getSource() pour savoir de quel bouton il
s'agit...
Je ne vais pas tout vous dire... Il faut que vous
cherchiez par vous-mêmes... La réflexion est très bonne !
Par contre, je vous
le dis tout de suite :
la correction que je vous fournis n'est pas
LA correction.
Il n'y a pas de
bonne solution : si, en fait, considérez un programme qui fonctionne comme une
solution.
Je vous propose seulement une solution
POSSIBLE.
Allez, en avant mes ZérOs !
Correction
Vous avez bien réfléchi ?
Vous vous êtes brûlé
quelques neurones... Vous avez mérité votre correction...
Secret (cliquez
pour afficher)
Classe Main.java
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
Calculatrice calculette = new Calculatrice();
}
}
|
Classe Calculatrice.java
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Calculatrice extends JFrame {
private JPanel container = new JPanel();
String[] tab_string = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ".", "=", "C", "+", "-", "*", "/"};
JButton[] tab_button = new JButton[tab_string.length];
private JLabel ecran = new JLabel();
private Dimension dim = new Dimension(50, 40);
private Dimension dim2 = new Dimension(50, 31);
private double chiffre1;
private boolean clicOperateur = false, update = false;
private String operateur = "";
public Calculatrice(){
this.setSize(240, 260);
this.setTitle("Calculette");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setResizable(false);
initComposant();
this.setContentPane(container);
this.setVisible(true);
}
private void initComposant(){
Font police = new Font("Arial", Font.BOLD, 20);
ecran = new JLabel("0");
ecran.setFont(police);
ecran.setHorizontalAlignment(JLabel.RIGHT);
ecran.setPreferredSize(new Dimension(220, 20));
JPanel operateur = new JPanel();
operateur.setPreferredSize(new Dimension(55, 225));
JPanel chiffre = new JPanel();
chiffre.setPreferredSize(new Dimension(165, 225));
JPanel panEcran = new JPanel();
panEcran.setPreferredSize(new Dimension(220, 30));
for(int i = 0; i < tab_string.length; i++)
{
tab_button[i] = new JButton(tab_string[i]);
tab_button[i].setPreferredSize(dim);
switch(i){
case 11 :
tab_button[i].addActionListener(new EgalListener());
chiffre.add(tab_button[i]);
break;
case 12 :
tab_button[i].setForeground(Color.red);
tab_button[i].addActionListener(new ResetListener());
operateur.add(tab_button[i]);
break;
case 13 :
tab_button[i].addActionListener(new PlusListener());
tab_button[i].setPreferredSize(dim2);
operateur.add(tab_button[i]);
break;
case 14 :
tab_button[i].addActionListener(new MoinsListener());
tab_button[i].setPreferredSize(dim2);
operateur.add(tab_button[i]);
break;
case 15 :
tab_button[i].addActionListener(new MultiListener());
tab_button[i].setPreferredSize(dim2);
operateur.add(tab_button[i]);
break;
case 16 :
tab_button[i].addActionListener(new DivListener());
tab_button[i].setPreferredSize(dim2);
operateur.add(tab_button[i]);
break;
default :
chiffre.add(tab_button[i]);
tab_button[i].addActionListener(new ChiffreListener());
break;
}
}
panEcran.add(ecran);
panEcran.setBorder(BorderFactory.createLineBorder(Color.black));
container.add(panEcran, BorderLayout.NORTH);
container.add(chiffre, BorderLayout.CENTER);
container.add(operateur, BorderLayout.EAST);
}
private void calcul(){
if(operateur.equals("+"))
{
chiffre1 = chiffre1 + Double.valueOf(ecran.getText()).doubleValue();
ecran.setText(String.valueOf(chiffre1));
}
if(operateur.equals("-"))
{
chiffre1 = chiffre1 - Double.valueOf(ecran.getText()).doubleValue();
ecran.setText(String.valueOf(chiffre1));
}
if(operateur.equals("*"))
{
chiffre1 = chiffre1 * Double.valueOf(ecran.getText()).doubleValue();
ecran.setText(String.valueOf(chiffre1));
}
if(operateur.equals("/"))
{
try{
chiffre1 = chiffre1 / Double.valueOf(ecran.getText()).doubleValue();
ecran.setText(String.valueOf(chiffre1));
}catch(ArithmeticException e){
ecran.setText("0");
}
}
}
class ChiffreListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
//On affiche le chiffre en plus dans le label
String str = ((JButton)e.getSource()).getText();
if(update)
{
update = false;
}
else
{
if(!ecran.getText().equals("0"))
str = ecran.getText() + str;
}
ecran.setText(str);
}
}
class EgalListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent arg0) {
calcul();
update = true;
clicOperateur = false;
}
}
class PlusListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent arg0) {
if(clicOperateur)
{
calcul();
ecran.setText(String.valueOf(chiffre1));
}
else
{
chiffre1 = Double.valueOf(ecran.getText()).doubleValue();
clicOperateur = true;
}
operateur = "+";
update = true;
}
}
class MoinsListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent arg0) {
if(clicOperateur)
{
calcul();
ecran.setText(String.valueOf(chiffre1));
}
else
{
chiffre1 = Double.valueOf(ecran.getText()).doubleValue();
clicOperateur = true;
}
operateur = "-";
update = true;
}
}
class MultiListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent arg0) {
if(clicOperateur)
{
calcul();
ecran.setText(String.valueOf(chiffre1));
}
else
{
chiffre1 = Double.valueOf(ecran.getText()).doubleValue();
clicOperateur = true;
}
operateur = "*";
update = true;
}
}
class DivListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent arg0) {
if(clicOperateur)
{
calcul();
ecran.setText(String.valueOf(chiffre1));
}
else
{
chiffre1 = Double.valueOf(ecran.getText()).doubleValue();
clicOperateur = true;
}
operateur = "/";
update = true;
}
}
class ResetListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent arg0) {
clicOperateur = false;
update = true;
chiffre1 = 0;
operateur = "";
ecran.setText("");
}
}
}
|
Je ne
m'attarderai pas trop là-dessus, ce code est très simple et je suis sûr que vous
trouverez des améliorations

.
Regardez bien comment tout interagit, et vous comprendrez comment il
fonctionne !
Je vais vous donner une petite astuce pour faire un
.jar exécutable en java...
Astuce Eclipse : faire un jar exécutable
Tout d'abord, qu'est-ce qu'un
.jar ?
Un tel fichier est une archive Java
(
Java
ARchive). Ce type de fichier contient
tout ce dont a besoin la JVM pour lancer votre programme ! Une fois votre
archive créée, il vous suffira juste de double cliquer sur celle-ci pour lancer
l'application !
Vous allez voir que créer un
.jar est très simple !
Commencez par faire un clic
droit sur votre projet et choisissez l'option
Export,
comme ceci :
Vous
voici dans la gestion des exports. Eclipse vous demande quel type d'export vous
souhaitez faire :
Comme
sur l'image ci-dessus, ssélectionnez
JAR File et
cliquez sur
Next. Vous voici maintenant dans le
section qui vous demande quels fichiers vous voulez mettre dans votre
archive.
- Dans le cadre vert, nous choisissons tous les fichiers qui composeront
notre exécutable .jar.
- Dans le cadre bleu, nous spécifions à eclipse où créer l'archive et quel
nom elle doit avoir.
- Ensuite... On clique sur Next.
La page suivante n'est pas pertinente ici. Allez hop !
Circulez, y a rien à voir... (Il manque une émoticône gendarme sur le
SDZ...)
La voici tout de même :
Cliquez
sur
Next et nous arrivons sur la page qui va nous
demander où se trouve la méthode
main dans notre programme :
Cliquez
sur
Browse... ; vous êtes maintenant sur une petite
popup qui liste les fichiers de programmes contenant une méthode
main. Ici, nous n'en avons qu'un... Mais
il est possible d'avoir plusieurs méthodes main
déclarées MAIS SEULEMENT UNE SEULE SERA EXÉCUTÉE
!
Sélectionnez
le point de départ de votre application et validez !
Voici ce que nous avons
:
Vous
pouvez maintenant cliquez sur
Finish et vous devriez
avoir un message du genre...
Ce
type de message n'est pas alarmant ! Celui-ci vous dit qu'il existe des choses
qu'Eclipse ne juge pas très claires... Mais celles-ci n'empêcheront pas votre
application de fonctionner... Contrairement à un message d'erreur... Mais là,
vous allez le repérer... Il est rouge.
Une fois cette étape validée, vous
pouvez voir avec joie, qu'un fichier
.jar a bien été
généré dans le dossier spécifié !
Double
cliquez sur lui et votre calculatrice se lance !

Si
vous voulez en savoir plus sur les fichiers
.jar, je
vous recommande le
tuto
de bartimeus.
J'espère que ce TP vous a plu !
Je vous le répète
encore une fois, mais mon code est loin d'être parfait et vous pouvez vous y
attarder pour l'améliorer !

Par
exemple, vous pourriez créer un objet dont la fonction est de faire les
calculs...
Vous pourriez aussi faire une calculette avec plus de
fonctionnalités ! Enfin, vous voyez quoi...
Bon, continuons notre tuto.
Nous en étions restés au problème de
thread évoqué
lorsque nous avions essayé de relancer notre animation.
Je pense que vous
avez deviné la suite, en avant pour :
les
threads.
Les threads
Dans ce chapitre, nous allons voir comment créer une nouvelle
pile de fonctions et même plusieurs ; tout ceci avec ce qu'on appelle des
threads.
Il y a une classe
Thread dans java qui gère cela, mais vous
allez voir qu'il y a en fait deux façons de créer un nouveau thread, et donc une
nouvelle pile d'invocation de méthodes !

Ne
tardons pas...
Principes et bases
Je vous le répète encore mais, lorsque vous lancez
votre programme, il y a un thread lancé ! Imaginez que votre thread corresponde
à la pile, et,
pour chaque nouveau thread créé, celui-ci donne une
nouvelle pile d'exécution.Pour le moment, nous n'allons pas
travailler avec notre IHM, nous allons revenir en mode console. Créez-vous un
nouveau projet et une classe contenant votre méthode
main. Maintenant, testez ce code :
Code : Java
1
2
3
4
5
6
7
8 |
public class Test {
public static void main(String[] args) {
System.out.println("Le nom du thread principal est " + Thread.currentThread().getName());
}
}
|
Vous devriez obtenir ceci
:
Code : Console
|
Le nom du thread principal est main |
Oui,
vous ne rêvez pas... Il s'agit bien de notre méthode
main, c'est le thread principal de notre
application !
Voyez les threads comme une machine bien huilée capable
d'effectuer les tâches que vous lui spécifierez. Une fois instancié, un thread
attend son lancement ; une fois celui-ci fait, il invoque sa méthode
run() ; c'est dans cette méthode
que le thread connaît les tâches qu'il a à faire !
Nous allons maintenant
voir comment créer un nouveau thread.
Comme je vous l'ai dit dans
l'introduction, il existe deux manières de faire :
- faire une classe héritée de la classe Thread ;
- créer une implémentation de l'interface Runnable et instancier un objet
Thread
avec une implémentation de cette interface.
Vous devez avoir les
sourcils qui se lèvent, là ... un peu comme ça :
Ne vous en faites pas, nous allons y aller
crescendo...
Une classe héritée de Thread
Nous allons commencer par le plus simple à
comprendre.
Comme je vous le disais, nous allons créer un classe héritée, et
tout ce que nous avons à faire, c'est redéfinir la méthode
run() de notre objet afin qu'il sache quoi
faire... Vu que nous allons en utiliser plusieurs, autant pouvoir les
différencier par un nom...
Créez la classe correspondant à ce diagramme :
On crée ici un constructeur avec un String en paramètre
pour spécifier le nom du thread... Cette classe a une méthode getName() afin de retourner celui-ci. La classe
Thread se
trouve dans la package java.lang, aucune instruction
import
n'est nécessaire !
Voilà le code de cette classe :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
public class TestThread extends Thread {
public TestThread(String name){
super(name);
}
public void run(){
for(int i = 0; i < 10; i++)
System.out.println(this.getName());
}
}
|
Et maintenant, testez ce
code plusieurs fois :
Code : Java
1
2
3
4
5
6
7
8
9
10 |
public class Test {
public static void main(String[] args) {
TestThread t = new TestThread("A");
TestThread t2 = new TestThread(" B");
t.start();
t2.start();
}
}
|
Voici quelques screenshots
de mes tests consécutifs :
Vous
pouvez voir que l'ordre d'exécution est totalement aléatoire !
Ceci car java
utilise un
ordonnanceur.
Vous devez
savoir que si vous utilisez plusieurs threads dans une application, ceux-ci
ne s'exécutent pas en même temps !
En fait, l'ordonnanceur gère les différents thread de façon
aléatoire : il va en utiliser un, pendant un certain laps de temps, puis un
autre, puis revenir au premier... Jusqu'à ce que les threads soit terminés ! Et,
lorsque l'ordonnanceur passe d'un thread à un autre, le thread interrompu est
mis en
sommeil pendant que l'autre est
en
éveil !
Un thread peut avoir
plusieurs états :
- NEW : lors de sa création.
- RUNNABLE : lorsque vous
invoquez la méthode start(),
celui-ci est prêt à travailler.
- TERMINATED : lorsque celui-ci
a terminé toutes ses tâches, on dit aussi que le thread est mort. Une fois un thread mort,
vous ne pouvez plus le relancer avec la méthode start() !
- TIMED_WAITING : lorsque
celui-ci est en pause, quand vous utilisez la méthode sleep() par exemple.
- WAITING : en attente
indéfinie...
- BLOCKED : lorsque
l'ordonnanceur met un thread en sommeil pour en utiliser un autre... Le statut
de celui en sommeil est celui-ci.
Un thread est considéré comme terminé
lorsque la méthode run() est dépilé de sa pile
d'exécution !
En effet, une nouvelle pile d'exécution a, à
sa base, la méthode
run() de notre
thread... Une fois celle-ci dépilée, notre nouvelle pile est détruite
!
Notre thread principal crée un second thread, celui-ci se lance et crée
une pile avec comme base sa méthode
run() ; celle-ci appelle
methode, l'empile, fait tous les
traitements, et, une fois terminé, dépile cette dernière. La méthode
run() prend fin, la pile est
détruite !
Nous allons un peu modifier notre classe
TestThread afin
de voir les états de nos threads que nous pouvons récupérer grâce à la méthode
getState().
Voici notre
classe
TestThread modifiée :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 |
public class TestThread extends Thread {
Thread t;
public TestThread(String name){
super(name);
System.out.println("statut du thread " + name + " = " +this.getState());
this.start();
System.out.println("statut du thread " + name + " = " +this.getState());
}
public TestThread(String name, Thread t){
super(name);
this.t = t;
System.out.println("statut du thread " + name + " = " +this.getState());
this.start();
System.out.println("statut du thread " + name + " = " +this.getState());
}
public void run(){
for(int i = 0; i < 10; i++){
System.out.println("statut " + this.getName() + " = " +this.getState());
if(t != null)System.out.println("statut de " + t.getName() + " pendant le thread " + this.getName() +" = " +t.getState());
}
}
public void setThread(Thread t){
this.t = t;
}
}
|
Ainsi que notre
main :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
public class Test {
public static void main(String[] args) {
TestThread t = new TestThread("A");
TestThread t2 = new TestThread(" B", t);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("statut du thread " + t.getName() + " = " + t.getState());
System.out.println("statut du thread " + t2.getName() + " = " +t2.getState());
}
}
|
Et un jeu d'essai
représentatif :
Alors,
dans notre classe
TestThread, nous avons ajouté quelques
instructions d'affichage afin de voir l'état en cours de nos objets, mais nous
avons aussi ajouté un constructeur avec un
Thread en paramètre supplémentaire, ceci
afin de voir l'état de notre premier thread lors de l'exécution du second
!
Dans notre jeu d'essai vous pouvez voir les différents statuts qu'ont
pris nos threads... Et vous pouvez voir que le premier est
BLOCKED lorsque le second est en cours de
traitement, ce qui justifie ce dont je vous parlais :
les threads ne
s'exécutent pas en même temps !
Vous pouvez
voir aussi que les traitements effectués par nos threads sont en fait codés dans
la méthode
run(). Reprenez l'image
que j'ai utilisée :
"
un thread est une machine
bien huilée capable d'effectuer les tâches que vous lui
spécifierez".
Le fait de faire un objet hérité de
Thread
permet de créer un nouveau thread très facilement. Cependant, vous pouvez
procéder autrement, en redéfinissant uniquement ce que doit faire le nouveau
thread, ceci grâce à l'interface
Runnable. Et dans ce cas, ma métaphore
prend tout son sens :
vous ne redéfinissez que ce que doit faire la
machine et non pas la machine tout entière !
Utiliser l'interface Runnable
Le fait de ne redéfinir que ce que doit faire notre
nouveau thread a aussi un autre avantage... Le fait d'avoir une classe qui
n'hérite d'aucune autre ! Eh oui : dans notre précédent test, notre classe
TestThread ne pourra plus jamais hériter
d'une classe ! Tandis qu'avec une implémentation de
Runnable, rien
n'empêche votre classe d'hériter de JFrame, par exemple...
Trêve de
bavardages : codons notre implémentation de
Runnable ; vous ne devriez avoir aucun
problème à faire ceci sachant qu'il n'y a que la méthode
run() à redéfinir...
Pour cet exemple,
nous allons utiliser un exemple que j'ai trouvé intéressant lorsque j'ai appris
à me servir des threads...
Vous allez créer un objet
CompteEnBanque avec une
somme d'argent par défaut, disons 50, et une méthode pour retirer de l'argent
(
retraitArgent) et une méthode qui
retourne le solde (
getSolde).
Mais avant de retirer de l'argent,
nous irons vérifier que nous ne sommes pas à découvert...

Notre
thread va faire autant d'opérations que nous le souhaitons. Voici un petit
diagramme de classe résumant la situation :
Je
résume.
- Notre application peut avoir 1 ou plusieurs objets Thread.
- Ceux-ci ne peuvent avoir qu'un objet de type Runnable.
- Dans notre cas, nos objets Thread auront une implémentation de
Runnable : RunImpl.
- Celui-ci à un objet CompteEnBanque.
Voici
les codes source :
RunImpl.java
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
public class RunImpl implements Runnable {
private CompteEnBanque cb;
public RunImpl(CompteEnBanque cb){
this.cb = cb;
}
public void run() {
for(int i = 0; i < 25; i++){
if(cb.getSolde() > 0){
cb.retraitArgent(2);
System.out.println("Retrait effectué");
}
}
}
}
|
CompteEnBanque.java
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
public class CompteEnBanque {
private int solde = 100;
public int getSolde(){
if(this.solde < 0)
System.out.println("Vous êtes à découvert !");
return this.solde;
}
public void retraitArgent(int retrait){
solde = solde - retrait;
System.out.println("Solde = " + solde);
}
}
|
Test.java
Code : Java
1
2
3
4
5
6
7
8
9
10 |
public class Test {
public static void main(String[] args) {
CompteEnBanque cb = new CompteEnBanque();
Thread t = new Thread(new RunImpl(cb));
t.start();
}
}
|
Ce qui nous donne :
Rien
d'extraordinaire ici... Une simple boucle aurait fait la même chose...
Ajoutons un nom à notre implémentation et créez un deuxième thread utilisant
un deuxième compte.
Pensez à modifier votre implémentation afin que nous
puissions voir sur quel thread nous sommes.

Bon
: je suis sympa, voici les codes :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 |
public class RunImpl implements Runnable {
private CompteEnBanque cb;
private String name;
public RunImpl(CompteEnBanque cb, String name){
this.cb = cb;
this.name = name;
}
public void run() {
for(int i = 0; i < 50; i++){
if(cb.getSolde() > 0){
cb.retraitArgent(2);
System.out.println("Retrait effectué par " + this.name);
}
}
}
}
|
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13 |
public class Test {
public static void main(String[] args) {
CompteEnBanque cb = new CompteEnBanque();
CompteEnBanque cb2 = new CompteEnBanque();
Thread t = new Thread(new RunImpl(cb, "Cysboy"));
Thread t2 = new Thread(new RunImpl(cb2, "ZérO"));
t.start();
t2.start();
}
}
|
Pour vérifier que nos
threads fonctionnent, voici une partie de mon résultat :
Jusqu'ici,
rien de perturbant... Nous avons utilisé deux instances distinctes de
RunImpl
utilisant deux instances distinctes de
CompteEnBanque.
Mais d'après
vous, que ce passerait-il si nous utilisions le même instance de CompteEnBanque dans
deux threads différents ? Essayez plusieurs fois ce code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12 |
public class Test {
public static void main(String[] args) {
CompteEnBanque cb = new CompteEnBanque();
Thread t = new Thread(new RunImpl(cb, "Cysboy"));
Thread t2 = new Thread(new RunImpl(cb, "ZérO"));
t.start();
t2.start();
}
}
|
Voici juste deux morceaux de
résultats obtenus lors de l'exécution :
Vous
pouvez voir des incohérences monumentales !
J'imagine que vous avez été
comme moi au départ, vous pensiez que le compte aurait été débité de deux en
deux jusqu'à la fin, sans avoir ce genre d'abérrations, vu que nous utilisons le
même objet... Eh bien non !
Pourquoi ? Tout simplement parce que
l'ordonnanceur de java met les threads en sommeil quand il le veut et, lorsque
celui qui était en sommeil se réveille, il reprend le travail où il s'était
arrêté !
Voyons comment résoudre le problème.

Synchronisez vos threads
Tout est dans le titre !

En
gros, ce qu'il faut faire, c'est prévenir la JVM qu'un thread est en train
d'utiliser des données qu'un autre thread est susceptible d'utiliser !
Ainsi, lorsque l'ordonnanceur met un thread en sommeil et que celui-ci
traitait des données utilisables par un autre thread, ce thread garde la
priorité sur les données, et tant que celui-ci n'a pas terminé son travail, les
autres threads n'ont pas la possibilité d'y toucher.
Ceci s'appelle synchroniser les threads.
Comment fait-on ça ? Je sens que ça va être encore un
truc tordu !
Cette opération est très délicate et demande beaucoup de
compétences en programmation...
Voici à quoi ressemble votre méthode
retraitArgent synchronisée
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
public class CompteEnBanque {
private int solde = 100;
public int getSolde(){
if(this.solde < 0)
System.out.println("Vous êtes à découvert !");
return this.solde;
}
public synchronized void retraitArgent(int retrait){
solde = solde - retrait;
System.out.println("Solde = " + solde);
}
}
|
Il vous suffit d'ajouter le
mot clé
synchronized dans la déclaration de votre
méthode !
Grâce à ce mot clé, cette méthode est
inaccessible à un thread si celle-ci est déjà utilisée par un autre thread ! Les
threads cherchant à utiliser des méthodes déjà prises en charge par un autre
thread sont mises dans une "liste
d'attente".Je récapitule encore une fois, voici
un contexte ludique.
Je serai représenté par le thread A, vous par le thread
B et notre boulangerie favorite par la méthode synchronisée M. Voici ce qu'il se
passe :
- le thread A (moi) appelle la méthode M ;
- je commence par demander une baguette, la boulangère me la pose sur le
comptoir et commence à calculer le montant ;
- c'est là que le thread B (vous) cherche aussi à utiliser la méthode M,
cependant, elle est déjà prise par un thread (moi...) ;
- vous êtes donc mis en attente ;
- l'action revient sur moi (thread A) ; au moment de payer, je dois chercher
de la monnaie dans ma poche...
- au bout de quelques instant, je m'endors...
- l'action revient sur le thread B (vous)... Mais la méthode M n'est
toujours pas libérée du thread A... Remise en attente ;
- on revient sur le thread A qui arrive enfin à payer et à quitter la
boulangerie, la méthode M est libérée !
- le thread B (vous) peut enfin utiliser la méthode M ;
- et là, les threads C, D, E, F, G, H, I, J entrent dans la boulangerie...
- etc.
Je pense qu'avec ceci vous avez dû comprendre...
Dans un contexte informatique, il peut être pratique et sécurisé d'utiliser
des threads et des méthodes synchronisées lors d'accès à des services distants
tel qu'un serveur d'application, ou encore un SGBD...
Les threads, pour
soulager le thread principal et ne pas bloquer l'application pendant une tâche
et des méthodes synchronisées, pour la sécurité et l'intégrité des données
!
Je vous propose maintenant de retourner à notre animation qui n'attend
qu'un petit thread pour pouvoir fonctionner correctement !
Contrôlez votre animation
À partir de là, il n'y a rien de bien
compliqué...
Il nous suffit de créer un nouveau thread lorsqu'on clique sur
le bouton
Go en lui passant une implémentation de
Runnable
qui, elle, va appeler la méthode
go() (ne pas oublier de remettre le booléen de
controle à
true).
Pour l'implémentation de l'interface Runnable, une classe interne est toute indiquée
!
Voici le code de notre classe
Fenetre avec le thread :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JButton bouton = new JButton("Go");
private JButton bouton2 = new JButton("Stop");
private JPanel container = new JPanel();
private JLabel label = new JLabel("Le JLabel");
private int compteur = 0;
private boolean animated = true;
private boolean backX, backY;
private int x,y ;
private Thread t;
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
//Ce sont maintenant nos classes internes qui écoutent nos boutons
bouton.addActionListener(new BoutonListener());
bouton2.addActionListener(new Bouton2Listener());
bouton2.setEnabled(false);
JPanel south = new JPanel();
south.add(bouton);
south.add(bouton2);
container.add(south, BorderLayout.SOUTH);
Font police = new Font("Tahoma", Font.BOLD, 16 );
label.setFont(police);
label.setForeground(Color.blue);
label.setHorizontalAlignment(JLabel.CENTER);
container.add(label, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
private void go(){
//Les coordonnées de départ de notre rond
x = pan.getPosX();
y = pan.getPosY();
//Pour cet exemple, j'utilise une boucle while
//Vous verrez qu'elle marche très bien
while(this.animated){
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* classe qui écoute notre bouton
*/
public class BoutonListener implements ActionListener{
/**
* Redéfinitions de la méthode actionPerformed
*/
public void actionPerformed(ActionEvent arg0) {
animated = true;
t = new Thread(new PlayAnimation());
t.start();
bouton.setEnabled(false);
bouton2.setEnabled(true);
}
}
/**
* classe qui écoute notre bouton2
*/
class Bouton2Listener implements ActionListener{
/**
* Redéfinitions de la méthode actionPerformed
*/
public void actionPerformed(ActionEvent e) {
animated = false;
bouton.setEnabled(true);
bouton2.setEnabled(false);
}
}
class PlayAnimation implements Runnable{
@Override
public void run() {
go();
}
}
}
|
Vous pouvez tester et tester
encore, ce code fonctionne très bien ! Vous avez enfin le contrôle sur votre
animation !

Ceci
fait, nous pouvons allez faire un tour sur le topo, et je crois qu'un QCM vous
attend...
Ce qu'il faut retenir
- Un nouveau thread permet de créer une nouvelle pile
d'exécution.
- La classe Thread
et l'interface Runnable se trouvent dans le package
java.lang, donc aucun import spécifique
est nécessaire pour leur utilisation.
- Un thread se lance lorsqu'on invoque la méthode start().
- Cette dernière invoque automatiquement la méthode run().
- Les traitements que vous souhaitez mettre dans une autre pile d'exécution
sont à mettre dans la méthode run(), qu'il
s'agisse d'une classe héritée de Thread ou d'une implémentation de
Runnable.
- Pour protéger l'intégrité de vos données accessibles à plusieurs threads,
utilisez le mot clé synchronized dans la déclaration de vos
méthodes.
- Un thread est déclaré mort lorsque celui-ci a
dépilé sa méthode run() de sa pile d'exécution
!
- Les threads peuvent avoir plusieurs états : BLOCKED, NEW, TERMINATED, WAITING, RUNNABLE et TIMED_WATING.
Voilà encore un gros chapitre, et très important qui
plus est !
Prenez le temps de bien assimiler les choses et de faire des
tests, plein de tests : c'est la meilleure façon de bien comprendre les
choses...
Pour ceux qui se sentent d'attaque, en avant pour :
les listes.
Les listes : l'objet JComboBox
Nous allons continuer à explorer les objets que nous propose
swing.
Ici, nous parlerons des listes de choix ou
JComboBox, nous pourrons ainsi améliorer
notre animation...

Let's
go, ZérO boys !
Première utilisation
Comme à l'accoutumée, nous allons d'abord utiliser cet
objet dans un contexte vierge de tout code... Alors créez-vous un projet avec
une classe contenant la méthode
main et une classe qui sera héritée de
JFrame.
Dans cet exemple, nous
allons bien évidemment utiliser une liste, donc créez en une...
Voici
un premier code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JComboBox combo = new JComboBox();
private JLabel label = new JLabel("Une ComboBox");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
top.add(label);
top.add(combo);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
}
|
Et l'aperçu :
Vous
constatez que votre objet est ridiculement petit...

Mais
vous connaissez le remède !
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JComboBox combo = new JComboBox();
private JLabel label = new JLabel("Une ComboBox");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
combo.setPreferredSize(new Dimension(100,20));
JPanel top = new JPanel();
top.add(label);
top.add(combo);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
}
|
Et voilà :
Et comment on renseigne cette liste ?
Nous y
voilà ! Pour ça, il suffit d'utiliser la méthode
addItem(Object
obj).
Vous devez savoir que lorsque l'objet affiche les
éléments ajoutés, celui-ci appelle la méthode toString() des objets ajoutés... Dans cet
exemple, nous avons utilisé des objets String, mais essayez avec un autre objet et
vous verrez...
Voici le nouveau code :
Code
: Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JComboBox combo = new JComboBox();
private JLabel label = new JLabel("Une ComboBox");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
combo.setPreferredSize(new Dimension(100,20));
combo.addItem("Option 1");
combo.addItem("Option 2");
combo.addItem("Option 3");
combo.addItem("Option 4");
JPanel top = new JPanel();
top.add(label);
top.add(combo);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
}
|
Et le résultat :
Vous pouvez utiliser le constructeur qui prend un
tableau d'objets en paramètre afin de renseigner tous les éléments d'un coup
!
Ceci est donc équivalent :
Code :
Java
1
2 |
String[] tab = {"Option 1", "Option 2", "Option 3", "Option 4"};
combo = new JComboBox(tab);
|
Vous pouvez présélectionner
un choix avec la méthode
setSelectedIndex(int index). Vous avez aussi la possibilité
de changer la couleur d'écriture, la couleur de fond, la police exactement comme
un
JLabel.
Maintenant que vous voyez
comment fonctionne cet objet, nous allons voir comment communiquer avec lui.
L'interface ItemListener
Cette interface à une méthode à redéfinir. Celle-ci est
appelée lorsqu'un élément a changé d'état !
Vu qu'un exemple est toujours
plus éloquent, voici un code implémentant cette interface :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JComboBox combo = new JComboBox();
private JLabel label = new JLabel("Une ComboBox");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
String[] tab = {"Option 1", "Option 2", "Option 3", "Option 4"};
combo = new JComboBox(tab);
//Ajout du listener
combo.addItemListener(new ItemState());
combo.setPreferredSize(new Dimension(100,20));
combo.setForeground(Color.blue);
JPanel top = new JPanel();
top.add(label);
top.add(combo);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
/**
* Classe interne implémentant l'interface ItemListener
*/
class ItemState implements ItemListener{
public void itemStateChanged(ItemEvent e) {
System.out.println("événement déclenché sur : " + e.getItem());
}
}
}
|
Dans mon exemple, j'ai
cliqué sur :
- Option 2, puis
- Option 3, puis
- Option 4.
Ce qui me donne :
Vous
voyez bien que lorsque vous cliquez sur une option non sélectionnée, notre objet
change d'abord l'état de l'option précédente (l'état passe en
DESELECTED) avant de changer l'état de l'option
choisie (celle-ci passe à l'état
SELECTED).
Vous pouvez donc suivre très
facilement l'état de vos éléments grâce à cette interface ; cependant, pour plus
de simplicité, nous utiliserons l'interface
ActionListener pour récupérer l'option
sélectionnée !
Voici un code implémentant cette interface :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JComboBox combo = new JComboBox();
private JLabel label = new JLabel("Une ComboBox");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
String[] tab = {"Option 1", "Option 2", "Option 3", "Option 4"};
combo = new JComboBox(tab);
//Ajout du listener
combo.addItemListener(new ItemState());
combo.addActionListener(new ItemAction());
combo.setPreferredSize(new Dimension(100,20));
combo.setForeground(Color.blue);
JPanel top = new JPanel();
top.add(label);
top.add(combo);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
/**
* Classe interne implémentant l'interface ItemListener
*/
class ItemState implements ItemListener{
public void itemStateChanged(ItemEvent e) {
System.out.println("ItemListener : événement déclenché sur : " + e.getItem());
}
}
class ItemAction implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("ActionListener : action sur " + combo.getSelectedItem());
}
}
}
|
Et le résultat :
Vous
constatez qu'avec une telle implémentation, nous pouvons récupérer l'option sur
laquelle l'action a été produite. L'appel de la méthode
getSelectedItem() retourne la valeur de l'option
sélectionnée ; une fois ceci récupérée, nous allons pouvoir travailler avec
notre liste !
Maintenant que nous savons comment récupérer les
informations dans une liste, je vous invite à continuer notre animation.
Changer la forme de notre animation
Comme le titre l'indique, nous allons faire en sorte
que notre animation ne se contente plus d'afficher un rond... Nous allons
pouvoir choisir quelle forme nous voulons afficher !

Bien
sûr, je ne vais pas vous faire réaliser toutes les formes possibles et
imaginables... Je vous en fournis quelques-unes et, si le coeur vous en dit,
ajoutez-en de votre composition !
Très bien : pour arriver à faire ceci,
nous allons dynamiser un peu notre classe
Panneau, celle-ci devra pouvoir peindre
différentes forme selon notre bon vouloir.
Pour y parvenir, nous allons
ajouter une variable d'instance de type
String qui contiendra l'intitulé de la
forme que nous souhaitons dessiner - appelons-la
forme - et ajoutons un mutateur afin de pouvoir
redéfinir cette variable.
Notre méthode
paintComponent doit pouvoir dessiner la forme
demandée ; ici, deux cas de figures se profilent :
- soit nous intégrons les instructions if dans cette méthode et l'objet
Graphics
dessinera en fonction,
- soit nous développons une méthode privée, appelée dans la méthode
paintComponent, et qui dessinera
la forme demandée.
J'ai l'habitude de procéder de la deuxième
façon pour éviter les conditions à rallonges dans mes méthodes...
Faites toutefois attention lorsque vous faites ceci ! Il
se peut que votre classe devienne énorme et que vous vous y perdiez ! Nous
verrons, dans une partie traitant des
design
patterns, que lorsque votre code a des portions de code
contenant beaucoup de conditions, il vaut mieux programmer une implémentation,
mais nous n'en sommes pas encore là...
Nous allons donc développer une méthode privée, appelons-la
draw(Graphics
g), qui aura pour tâche de dessiner la forme voulue. Nous
passerons l'objet
Graphics
utilisé dans la méthode
paintComponent afin qu'elle puisse l'utiliser, et
c'est dans cette méthode que nous mettrons nos conditions.
Je vous
propose les formes suivantes :
- le rond, forme par défaut,
- le carré,
- le triangle,
- l'étoile (soyons fous
).
Ce qui veut dire que notre liste contiendra ces choix, et
le rond sera le premier !
Nous créerons aussi une implémentation
d'
ActionListener dans une classe interne
pour gérer les actions sur notre liste ; je l'ai appelée
FormeListener, c'est
fou ce que je suis original...
Une fois n'est pas coutume, voici ce que
vous devez obtenir :
Essayez
de trouver comment faire ces formes... Il n'y a rien de compliqué, je vous
assure ! Bon, l'étoile est peut-être un peu plus difficile que le reste, mais
elle n'est pas insurmontable...

Voici
les codes :
Secret (cliquez
pour afficher)
La classe
Panneau Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 |
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class Panneau extends JPanel {
private int posX = -50;
private int posY = -50;
private String forme = "ROND";
public void paintComponent(Graphics g){
//On décide d'une couleur de fond pour notre rectangle
g.setColor(Color.white);
//On dessine celui-ci afin qu'il prenne toute la surface
g.fillRect(0, 0, this.getWidth(), this.getHeight());
//On redéfinit une couleur pour notre rond
g.setColor(Color.red);
//On délègue la méthode de dessin à la méthode draw()
draw(g);
}
private void draw(Graphics g){
if(this.forme.equals("ROND")){
g.fillOval(posX, posY, 50, 50);
}
if(this.forme.equals("CARRE")){
g.fillRect(posX, posY, 50, 50);
}
if(this.forme.equals("TRIANGLE")){
//calcul des sommets
//Le sommet 1 est à la moitié du côté supérieur du carré de 50
int s1X = posX + 25;
int s1Y = posY;
//Le sommet deux est en bas à droite
int s2X = posX + 50;
int s2Y = posY + 50;
//Le sommet Trois en bas à gauche
int s3X = posX;
int s3Y = posY + 50;
// Nous créons deux tableaux de coordonnées
int[] ptsX = {s1X, s2X, s3X};
int[] ptsY = {s1Y, s2Y, s3Y};
//Et nous utilisons la méthode fillPolygon
g.fillPolygon(ptsX, ptsY, 3);
}
if(this.forme.equals("ETOILE")){
//Pour l'étoile, je me contente de tracer des lignes
//dans le carré correspondant à peu près à une étoile...
//Mais ce code peut être amélioré !
int s1X = posX + 25;
int s1Y = posY;
int s2X = posX + 50;
int s2Y = posY + 50;
g.drawLine(s1X, s1Y, s2X, s2Y);
int s3X = posX;
int s3Y = posY + 17;
g.drawLine(s2X, s2Y, s3X, s3Y);
int s4X = posX + 50;
int s4Y = posY + 17;
g.drawLine(s3X, s3Y, s4X, s4Y);
int s5X = posX;
int s5Y = posY + 50;
g.drawLine(s4X, s4Y, s5X, s5Y);
g.drawLine(s5X, s5Y, s1X, s1Y);
}
}
public void setForme(String form){
this.forme = form;
}
public int getPosX() {
return posX;
}
public void setPosX(int posX) {
this.posX = posX;
}
public int getPosY() {
return posY;
}
public void setPosY(int posY) {
this.posY = posY;
}
}
|
La classe
Fenetre Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JButton bouton = new JButton("Go");
private JButton bouton2 = new JButton("Stop");
private JPanel container = new JPanel();
private JLabel label = new JLabel("Choix de la forme");
private int compteur = 0;
private boolean animated = true;
private boolean backX, backY;
private int x,y ;
private Thread t;
private JComboBox combo = new JComboBox();
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
bouton.addActionListener(new BoutonListener());
bouton2.addActionListener(new Bouton2Listener());
bouton2.setEnabled(false);
JPanel south = new JPanel();
south.add(bouton);
south.add(bouton2);
container.add(south, BorderLayout.SOUTH);
combo.addItem("ROND");
combo.addItem("CARRE");
combo.addItem("TRIANGLE");
combo.addItem("ETOILE");
combo.addActionListener(new FormeListener());
JPanel top = new JPanel();
top.add(label);
top.add(combo);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
private void go(){
x = pan.getPosX();
y = pan.getPosY();
while(this.animated){
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* classe qui écoute notre bouton
*/
public class BoutonListener implements ActionListener{
/**
* Redéfinitions de la méthode actionPerformed
*/
public void actionPerformed(ActionEvent arg0) {
animated = true;
t = new Thread(new PlayAnimation());
t.start();
bouton.setEnabled(false);
bouton2.setEnabled(true);
}
}
/**
* classe qui écoute notre bouton2
*/
class Bouton2Listener implements ActionListener{
/**
* Redéfinitions de la méthode actionPerformed
*/
public void actionPerformed(ActionEvent e) {
animated = false;
bouton.setEnabled(true);
bouton2.setEnabled(false);
}
}
class PlayAnimation implements Runnable{
public void run() {
go();
}
}
class FormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//la méthode retourne un Object puisque nous passons des Object dans une liste
//Il faut donc utiliser la méthode toString() pour retourner un String (ou utiliser un cast)
pan.setForme(combo.getSelectedItem().toString());
}
}
}
|
Et voilà
le travail !
Vous avez vu qu'il n'y avait rien de sorcier ici. En fait, vu
que vous avez l'habitude d'utiliser des objets graphiques et des implémentations
d'interfaces, les choses vont s'accélérer car le principe reste le même pour
tous les objets graphiques de base !

Bon,
j'ai tout de même fait un léger topo et un mini-QCM...
Ce qu'il faut retenir
- L'objet JComboBox
est dans le package javax.swing.
- Vous pouvez ajouter des éléments dans une liste avec la méthode
addItem(Object
obj).
- Vous pouvez aussi instancier une liste avec un tableau de données.
- L'interface ItemListener
vous permet de gérer les états de vos éléments.
- Vous pouvez aussi utiliser l'interface ActionListener.
- La méthode getSelectedItem()
retourne une variable de type Object, pensez donc à faire un cast ou
utiliser la méthode toString() si
les éléments sont de ce type.
Rien de bien méchant ici, vous êtes habitués à utiliser
les objets swing et les interfaces...
Continuons
notre petit tour parmi les objets du package javax.swing, alors ! En route vers : les cases à cocher !
Les cases à cocher : l'objet JCheckBox
Nous allons ici aborder un autre objet, mais comme je vous
l'ai dit, le principe de fonctionnement est le même pour tous !
Nous allons
donc pouvoir accélérer notre rythme d'apprentissage !

Au
passage, il y a un autre objet qui s'utilise
quasiment de la même manière que celui-ci, nous le verrons
donc en même temps dans ce chapitre !
Premier exemple
Avant de commencer, nous allons nous familiariser avec
cet objet dans un environnement vierge... Comme d'habitude

.
Créez donc un projet vide avec une classe contenant une méthode
main et une classe héritée de
JFrame.
Ceci
fait, nous allons utiliser notre nouvel objet. Celui-ci peut être instancié avec
un String en paramètre, ce paramètre servira de libellé à notre objet. Vu qu'il
n'y a rien de compliqué à faire ceci, voici le code et le résultat :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 |
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JCheckBox check1 = new JCheckBox("Case 1");
private JCheckBox check2 = new JCheckBox("Case 2");
private JLabel label = new JLabel("Une ComboBox");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
top.add(check1);
top.add(check2);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
}
|
Résultat :
Vous
pouvez aussi cocher la case en appelant la méthode
setSelected(boolean
bool) en lui passant
true.
Cet objet a, comme tous les
autres, une multitude de méthodes afin de pouvoir travailler, je vous invite
aussi à fouiner un peu...

Créez
maintenant une implémentation de l'interface
ActionPerformed, vous connaissez bien la
marche à suivre... Et contrôlez que votre objet est coché ou non avec la méthode
isSelected() qui retourne un
booléen ; voici un code mettant en oeuvre cette demande :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JCheckBox check1 = new JCheckBox("Case 1");
private JCheckBox check2 = new JCheckBox("Case 2");
private JLabel label = new JLabel("Une ComboBox");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
check1.addActionListener(new StateListener());
check2.addActionListener(new StateListener());
top.add(check1);
top.add(check2);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
class StateListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("source : " + ((JCheckBox)e.getSource()).getText() + " - état : " + ((JCheckBox)e.getSource()).isSelected());
}
}
}
|
Résultat :
Ici,
je me suis amusé à cocher et décocher mes cases. Rien de bien difficile ici, ça
devient la routine...
Un pseudo-morphing pour notre animation
Nous allons utiliser cet objet afin de faire en sorte
que nos formes changent de taille et nous donnent un pseudo-effet de
morphing...
Que nous faut-il pour arriver à cela
?
Déjà, la taille de notre forme est fixe, il faut changer ça !
Allez, hop... Une variable de type
int dans notre classe
Panneau, disons
drawSize, initialisée à
50.
Tout comme le déplacement,
nous devons savoir quand augmenter ou réduire la taille de notre forme : nous
utiliserons la même méthode que pour le déplacement.
Un deuxième booléen
sera nécessaire pour savoir si le "
mode
morphing" est activé ou non...
En ce qui concerne la
taille, si on réduit (ou augmente) celle-ci d'une unité à chaque
rafraîchissement, l'effet de morphing sera
ULTRA-RAPIDE. Donc,
pour ralentir l'effet, nous allons utiliser une méthode qui va retourner
1 ou
0 selon le nombre de rafraîchissements. Ceci
signifie que nous allons avoir besoin d'une variable pour dénombrer ceux-ci.
Nous ferons une réduction (ou augmentation) tous les 10 tours !
Pour bien
séparer les deux cas de figure, nous allons faire une deuxième méthode de dessin
dans la classe
Panneau
qui aura pour rôle de dessiner le morphing ; appelons-la
drawMorph(Graphics
g).
Lorsque nous cocherons notre case à cocher, le
morphing sera lancé, et inversement ! La classe
Panneau devra donc avoir un mutateur pour
le booléen de morphing...

Cependant,
dans notre classe
Fenetre, nous gérons la collision avec les
bords... Mais en "
mode morphing",
la taille de notre forme n'est plus la même... Donc, il va falloir gérer ce
nouveau cas de figure dans notre méthode
go(). Notre classe
Panneau devra avoir un
accesseur pour la taille actuelle de la forme...
Vous avez tous les
éléments en main pour réussir ceci.
Voici ce que vous devriez obtenir (je
n'ai mis que le rond et le triangle... mais ça fonctionnera avec toutes les
formes) :
Voici
les codes :
Secret (cliquez
pour afficher)
Fichier Panneau.java
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 |
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class Panneau extends JPanel {
private int posX = -50;
private int posY = -50;
private int drawSize = 50;
//boolean pour le mode morphing et pour savoir si la taille doit réduire
private boolean morph = false, reduce = false;;
private String forme = "ROND";
//Le compteur de rafraîchissements
private int increment = 0;
public void paintComponent(Graphics g){
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
//Si le mode morphing est activé, on peint le morphing
if(this.morph)
drawMorph(g);
//sinon, mode normal
else
draw(g);
}
private void draw(Graphics g){
if(this.forme.equals("ROND")){
g.fillOval(posX, posY, 50, 50);
}
if(this.forme.equals("CARRE")){
g.fillRect(posX, posY, 50, 50);
}
if(this.forme.equals("TRIANGLE")){
int s1X = posX + 50/2;
int s1Y = posY;
int s2X = posX + 50;
int s2Y = posY + 50;
int s3X = posX;
int s3Y = posY + 50;
int[] ptsX = {s1X, s2X, s3X};
int[] ptsY = {s1Y, s2Y, s3Y};
g.fillPolygon(ptsX, ptsY, 3);
}
if(this.forme.equals("ETOILE")){
int s1X = posX + 50/2;
int s1Y = posY;
int s2X = posX + 50;
int s2Y = posY + 50;
g.drawLine(s1X, s1Y, s2X, s2Y);
int s3X = posX;
int s3Y = posY + 50/3;
g.drawLine(s2X, s2Y, s3X, s3Y);
int s4X = posX + 50;
int s4Y = posY + 50/3;
g.drawLine(s3X, s3Y, s4X, s4Y);
int s5X = posX;
int s5Y = posY + 50;
g.drawLine(s4X, s4Y, s5X, s5Y);
g.drawLine(s5X, s5Y, s1X, s1Y);
}
}
/**
* Méthode qui peint le morphing
* @param g
*/
private void drawMorph(Graphics g){
//On incrémente le tour
increment++;
//On regarde si on doit réduire ou non
if(drawSize >= 50)reduce = true;
if(drawSize <= 10)reduce = false;
if(reduce)
drawSize = drawSize - getUsedSize();
else
drawSize = drawSize + getUsedSize();
if(this.forme.equals("ROND")){
g.fillOval(posX, posY, drawSize, drawSize);
}
if(this.forme.equals("CARRE")){
g.fillRect(posX, posY, drawSize, drawSize);
}
if(this.forme.equals("TRIANGLE")){
int s1X = posX + drawSize/2;
int s1Y = posY;
int s2X = posX + drawSize;
int s2Y = posY + drawSize;
int s3X = posX;
int s3Y = posY + drawSize;
int[] ptsX = {s1X, s2X, s3X};
int[] ptsY = {s1Y, s2Y, s3Y};
g.fillPolygon(ptsX, ptsY, 3);
}
if(this.forme.equals("ETOILE")){
int s1X = posX + drawSize/2;
int s1Y = posY;
int s2X = posX + drawSize;
int s2Y = posY + drawSize;
g.drawLine(s1X, s1Y, s2X, s2Y);
int s3X = posX;
int s3Y = posY + drawSize/3;
g.drawLine(s2X, s2Y, s3X, s3Y);
int s4X = posX + drawSize;
int s4Y = posY + drawSize/3;
g.drawLine(s3X, s3Y, s4X, s4Y);
int s5X = posX;
int s5Y = posY + drawSize;
g.drawLine(s4X, s4Y, s5X, s5Y);
g.drawLine(s5X, s5Y, s1X, s1Y);
}
}
/**
* Méthode qui retourne le nombre à retrancher (ou ajouter) pour le morphing
* @return res
*/
private int getUsedSize(){
int res = 0;
//Si le nombre de tours est de 10
//On réinitialise l'incrément et on retourne 1
if(increment / 10 == 1){
increment = 0;
res = 1;
}
return res;
}
public int getDrawSize(){
return drawSize;
}
public boolean isMorph(){
return morph;
}
public void setMorph(boolean bool){
this.morph = bool;
//On réinitialise la taille
drawSize = 50;
}
public void setForme(String form){
this.forme = form;
}
public int getPosX() {
return posX;
}
public void setPosX(int posX) {
this.posX = posX;
}
public int getPosY() {
return posY;
}
public void setPosY(int posY) {
this.posY = posY;
}
}
|
Fichier Fenetre.java
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JButton bouton = new JButton("Go");
private JButton bouton2 = new JButton("Stop");
private JPanel container = new JPanel();
private JLabel label = new JLabel("Choix de la forme");
private int compteur = 0;
private boolean animated = true;
private boolean backX, backY;
private int x,y ;
private Thread t;
private JComboBox combo = new JComboBox();
private JCheckBox morph = new JCheckBox("Morphing");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
bouton.addActionListener(new BoutonListener());
bouton2.addActionListener(new Bouton2Listener());
bouton2.setEnabled(false);
JPanel south = new JPanel();
south.add(bouton);
south.add(bouton2);
container.add(south, BorderLayout.SOUTH);
combo.addItem("ROND");
combo.addItem("CARRE");
combo.addItem("TRIANGLE");
combo.addItem("ETOILE");
combo.addActionListener(new FormeListener());
morph.addActionListener(new MorphListener());
JPanel top = new JPanel();
top.add(label);
top.add(combo);
top.add(morph);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
private void go(){
x = pan.getPosX();
y = pan.getPosY();
while(this.animated){
//Si le mode morphing est activé, on utilise la taille actuelle de la forme
if(pan.isMorph())
{
if(x < 1)backX = false;
if(x > pan.getWidth() - pan.getDrawSize())backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight() - pan.getDrawSize())backY = true;
}
//Sinon, comme d'habitude
else
{
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
}
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent arg0) {
animated = true;
t = new Thread(new PlayAnimation());
t.start();
bouton.setEnabled(false);
bouton2.setEnabled(true);
}
}
class Bouton2Listener implements ActionListener{
public void actionPerformed(ActionEvent e) {
animated = false;
bouton.setEnabled(true);
bouton2.setEnabled(false);
}
}
class PlayAnimation implements Runnable{
public void run() {
go();
}
}
class FormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
pan.setForme(combo.getSelectedItem().toString());
}
}
class MorphListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Si la case est cochée, activation du mode morphing
if(morph.isSelected())pan.setMorph(true);
//Sinon, rien !
else pan.setMorph(false);
}
}
}
|
Alors ? Vous
en pensez quoi ?
J'aime bien, moi...
Vous voyez que l'utilisation des
JCheckBox est très
simple... Je vous propose maintenant d'aller voir un de ses cousins...
Les boutons radio : l'objet JRadioButton
Le voici, le cousin éloigné...
Bon, alors : le
principe est de pouvoir avoir deux (ou plus) choix distincts, mais qu'un seul
soit sélectionné à la fois... C'est-à-dire que pour un choix
oui / non, le choix
oui étant présélectionné, lorsque nous choisirons
non, l'autre choix se
désélectionnera tout seul...

L'instanciation
se fait de la même manière que pour un
JCheckBox ; d'ailleurs, nous allons
utiliser l'exemple du début de chapitre en remplaçant les cases à cocher par des
boutons radio. Voici les codes et le résultat :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JRadioButton jr1 = new JRadioButton("Radio 1");
private JRadioButton jr2 = new JRadioButton("Radio 2");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
jr1.addActionListener(new StateListener());
jr2.addActionListener(new StateListener());
top.add(jr1);
top.add(jr2);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
class StateListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("source : " + ((JRadioButton)e.getSource()).getText() + " - état : " + ((JRadioButton)e.getSource()).isSelected());
}
}
}
|
Résultat :
Vous
pouvez voir que ces objets s'utilisent de la même manière.
Euh... Tu nous as dis que seul un choix devrait être
sélectionné... Or ici, tes deux radios sont sélectionnés...
Tout à fait... Voilà la différence entre ces deux objets... Pour qu'un
seul bouton radio soit sélectionné à la fois, nous devons définir un groupe de
boutons,
ButtonGroup. Dans celui-ci, nous
ajouterons nos boutons radio et ainsi seul un bouton sera sélectionné à la fois
!
Code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JRadioButton jr1 = new JRadioButton("Radio 1");
private JRadioButton jr2 = new JRadioButton("Radio 2");
private ButtonGroup bg = new ButtonGroup();
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
jr1.setSelected(true);
jr1.addActionListener(new StateListener());
jr2.addActionListener(new StateListener());
bg.add(jr1);
bg.add(jr2);
top.add(jr1);
top.add(jr2);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
class StateListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("source : " + jr1.getText() + " - état : " + jr1.isSelected());
System.out.println("source : " + jr2.getText() + " - état : " + jr2.isSelected());
}
}
}
|
Résultat :
Une
précision, toutefois... Lorsque je vous ai dit que la différence entre ces
composants se trouvait dans cette notion de groupe de boutons... Je vous ai un
peu induits en erreur

.
En fait, j'ai dit ceci car il est plus fréquent d'avoir ce genre de chose
sur des boutons de type radio que sur un autre, mais rien ne vous empêche de
faire la même chose pour des cases à cocher...
Effectivement, l'objet
ButtonGroup
accepte en paramètre un type
AbstractButton qui est la super-classe de tous les
boutons... Vous pouvez donc en tirer les conclusions qui s'imposent !
Ce qu'il faut retenir
- Les objets JCheckBox, JRadioButton et
ButtonGroup
sont dans le package javax.swing
- Vous pouvez savoir si un de ces composants est sélectionné grâce à la
méthode isSelected().
- Cette méthode retourne true si l'objet est sélectionné, et
false
dans le cas contraire.
- Vous pouvez restreindre les choix dans un groupe de réponses en utilisant
la classe ButtonGroup.
- Vous pouvez ajouter des boutons à un groupe de boutons grâce à la méthode
add(AbstractButton
button).
Allez... Pas besoin de QCM
ici... Il n'y a rien de compliqué !
Je sens que vous êtes de plus en plus à l'aise avec la
programmation événementielle !
Continuons donc avec : les zones de texte.
Les champs de texte : l'objet JTextField
Dans ce chapitre, nous allons voir l'objet qui va vous
permettre de saisir des informations.
Celui-ci est très simple d'utilisation
aussi...
Ne perdons pas de temps, allons-y !

Utilisation
Je pense que vous savez ce qu'il vous reste à faire...
Donc, si ce n'est pas encore fait, créez-vous un nouveau projet avec les classes
habituelles.
Comme le titre du chapitre l'indique, nous allons utiliser
l'objet
JTextField. Comme vous pouvez vous en
douter, cet objet a lui aussi les méthodes de redimensionnement, de couleur de
police...
De ce fait, je commence donc avec un exemple complet. Regardez
et testez ce code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JTextField jtf = new JTextField("Valeur par défaut");
private JLabel label = new JLabel("Un JTextField");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
Font police = new Font("Arial", Font.BOLD, 14);
jtf.setFont(police);
jtf.setPreferredSize(new Dimension(150, 30));
jtf.setForeground(Color.BLUE);
top.add(label);
top.add(jtf);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
}
|
Ce qui nous donne :
Vous
pouvez voir que c'est très simple ! Vous pouvez saisir ce que vous voulez dans
cette zone de texte.
Vous pouvez initialiser le contenu avec la méthode
setText(String
str) ou le récupérer avec la méthode getText().
Il existe toutefois un
objet très ressemblant à celui-ci, un peu plus étoffé. En fait, cet objet permet
d'avoir un
JTextField formaté pour recevoir un
certain type de saisie (date, pourcentage...).
Voyons ça tout de suite...
Un objet plus restrictif : le JFormattedTextField
Avec ce genre d'objet, vous allez pouvoir vous éviter
beaucoup de contrôles et de cast sur le contenu de vos zones de
texte...
Si vous avez essayé de récupérer le contenu du
JTextField utilisé
ci-dessus (lors du clic sur un bouton, par exemple...) vous avez dû vous rendre
compte que le type de contenu lui était égal...
Mais, un jour sûrement,
vous aurez besoin d'une zone de texte qui n'accepte qu'un certain type de
donnée... Avec l'objet
JFormattedTextField, nous nous en
rapprochons, mais vous verrez que vous pouvez faire encore mieux !
Cet objet
retourne une valeur uniquement si celle-ci correspond à ce que vous lui avez
demandé de contenir, je m'explique : si vous voulez que votre zone de texte
DOIVE contenir des entiers, ou des dates... c'est possible !
Par contre, ce contrôle se fait lorsque vous quittez le
champ en question ! Vous pouvez saisir des lettres dans un objet
n'acceptant que des entiers, mais la méthode getText() ne renverra RIEN car le contenu sera effacé si
les données ne correspondent pas aux attentes ! !
Voici un
code et deux exemples :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JFormattedTextField jtf = new JFormattedTextField(NumberFormat.getIntegerInstance());
private JFormattedTextField jtf2 = new JFormattedTextField(NumberFormat.getPercentInstance());
private JLabel label = new JLabel("Un JTextField");
private JButton b = new JButton ("OK");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
Font police = new Font("Arial", Font.BOLD, 14);
jtf.setFont(police);
jtf.setPreferredSize(new Dimension(150, 30));
jtf.setForeground(Color.BLUE);
jtf2.setPreferredSize(new Dimension(150, 30));
b.addActionListener(new BoutonListener());
top.add(label);
top.add(jtf);
top.add(jtf2);
top.add(b);
// container.add(top, BorderLayout.NORTH);
this.setContentPane(top);
this.setVisible(true);
}
class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("TEXT : jtf " + jtf.getText());
System.out.println("TEXT : jtf2 " + jtf2.getText());
}
}
}
|
Exemple valide :
Exemple
invalide :
Vous pouvez voir que notre objet met aussi en forme
la saisie lorsque celle-ci est valide ! Celui-ci sépare les nombres 3 par 3 afin
de facilité la lecture...
Voici les types de contenus que vous
pouvez utiliser :
- NumberFormat
avec :
- getIntegerInstance()
- getPercentInstance()
- getNumberInstance()
- DateFormat
avec :
- getTimeInstance()
- getDateInstance()
- MessageFormat
Sans rentrer dans les détails, vous pouvez
aussi utiliser un objet
MaskFormatter qui permet d'avoir un format
à taille fixe dans votre zone de texte. Ceci est très pratique lorsque vous
voulez un numéro de téléphone, un numéro de sécurité sociale...
Vous devez
définir ce format en paramètre dans le constructeur à l'aide de
méta-caractères. Ceux-ci permettent de dire à
votre objet
MaskFormatter comment doit être constitué
le futur contenu de votre zone de texte. Voici la liste de ces
méta-caractères :
- # indique un chiffre ;
- ' indique un caractère
d'échappement ;
- U indique une lettre (les
minuscules sont changées en majuscules) ;
- L indique une lettre (les
majuscules sont changées en minuscules) ;
- A indique un chiffre ou une
lettre ;
- ? indique une lettre ;
- * indique que tous les
caractères sont acceptés ;
- H indique que tous les
caractères hexadécimaux sont acceptés (0->9, a->f ou A->F).
L'instanciation d'un tel objet peut lever une
ParseException. Vous devrez donc
l'entourer d'un bloc try{...}catch(ParseException
e){...}.
Voici à quoi ressemblerait un format
téléphonique :
Code : Java
1
2
3
4
5
6
7 |
try{
MaskFormatter tel = new MaskFormatter("## ## ## ## ##");
//Ou encore
MaskFormatter tel2 = new MaskFormatter("##-##-##-##-##");
//Vous pouvez ensuite le passer à votre zone de texte
JFormattedTextField jtf = new JFormattedTextField(tel2);
}catch(ParseException e){e.printStackTrace();}
|
Vous pouvez vous rendre
compte qu'il n'y a rien de compliqué...
Je vous donne tout de même un
exemple de code permettant de saisir un numéro de téléphone français et un
numéro de téléphone américain :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JFormattedTextField jtf;
private JFormattedTextField jtf2;
private JLabel label = new JLabel("Téléphone FR ");
private JLabel label2 = new JLabel("Téléphone USA");
private JButton b = new JButton ("OK");
/**
* Constructeur de l'objet
*/
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 150);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
try{
MaskFormatter tel = new MaskFormatter("##-##-##-##-##");
MaskFormatter telUSA = new MaskFormatter("###-####");
jtf = new JFormattedTextField(tel);
jtf2 = new JFormattedTextField(telUSA);
}catch(ParseException e){
e.printStackTrace();
}
JPanel top = new JPanel();
Font police = new Font("Arial", Font.BOLD, 14);
jtf.setFont(police);
jtf.setPreferredSize(new Dimension(150, 30));
jtf.setForeground(Color.BLUE);
jtf2.setPreferredSize(new Dimension(150, 30));
b.addActionListener(new BoutonListener());
top.add(label);
top.add(jtf);
top.add(label2);
top.add(jtf2);
top.add(b);
// container.add(top, BorderLayout.NORTH);
this.setContentPane(top);
this.setVisible(true);
}
class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("Téléphone FR : " + jtf.getText());
System.out.println("Téléphone USA : " + jtf2.getText());
}
}
}
|
Vous pouvez constater qu'avec le méta-caractère
utilisé avec notre objet MaskFormatter, nous sommes obligé de
saisir des chiffres !
Et voici le résultat lorsque nous cliquons
sur le bouton :
Je ne sais pas pour le numéro américain, mais le
numéro de téléphone français est loin d'être un numéro de téléphone valide !
Ah ! je savais que vous alliez remarquer ce petit détail, de
taille je vous l'accorde.
Nous voilà confrontés à un problème qui vous
hantera tant que vous programmerez :
L'intégrité de vos données !Comme
démontré ci-dessus, vous pouvez aider le plus possible l'utilisateur sur ce
qu'il doit renseigner comme données dans des champs, vous ne devrez
JAMAIS FAIRE UNE CONFIANCE AVEUGLE EN CELLES-CI ! C'est
simple : dans ma boîte, on part du principe de ne jamais faire confiance à
l'utilisateur !
Nous sommes obligés de faire une multitude de contrôles
en plus, mais les applications ont le mérite d'être un tant soit peu
sécurisées...
Qu'est-ce que nous pouvons faire dans le cas de ta
saisie ?
En réalité, beaucoup de choses :
- tester chaque élément de votre numéro ;
- tester le numéro en entier ;
- dans le cas ou vous n'utilisez pas de MaskFormatter, vérifier en plus que les saisies soient
numériques ;
- utiliser une expression régulière ;
- empêcher la saisie d'un type de caractères ;
- ...
En gros, vous devrez vérifier l'intégrité de vos
données et, dans le cas qui nous intéresse, l'intégrité de vos chaînes de
caractères, pendant ou après la saisie !
D'ailleurs, c'est ce que je vous
propose de faire, pas plus tard que maintenant !
Contrôlez vos données post-saisie
Afin de voir comment contrôler au mieux vos données,
nous allons travailler avec un
JFormattedTextField
acceptant tous types de caractères. Voici donc notre code :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JFormattedTextField jtf;
private JFormattedTextField jtf2;
private JLabel label = new JLabel("Téléphone FR ");
private JButton b = new JButton ("OK");
/**
* Constructeur de l'objet
*/
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 150);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
try{
MaskFormatter tel = new MaskFormatter("**-**-**-**-**");
jtf = new JFormattedTextField(tel);
}catch(ParseException e){
e.printStackTrace();
}
JPanel top = new JPanel();
Font police = new Font("Arial", Font.BOLD, 14);
jtf.setFont(police);
jtf.setPreferredSize(new Dimension(150, 30));
jtf.setForeground(Color.BLUE);
b.addActionListener(new BoutonListener());
top.add(label);
top.add(jtf);
top.add(b);
this.setContentPane(top);
this.setVisible(true);
}
class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("Téléphone FR : " + jtf.getText());
}
}
}
|
Maintenant, vous pouvez
saisir n'importe quoi dans ce qui devait être un numéro de téléphone.
Il
reste tout de même une restriction sur le nombre de caractères que doit prendre
le champ, ici 10, mais, mis à part ça, vous êtes libres de saisir ce que vous
voulez :
Première approche
Une méthode de contrôle, un peu compliquée au
final, consisterait à exploser la chaîne de caractères grâce à la méthode
split(String regex) et
tester les éléments un par un...
Cette méthode,
split(String regex) ,
permet de créer un tableau de
String à partir d'une chaîne de caractères
en l'explosant par rapport à l'expression régulière passée en paramètre. Un
exemple est toujours mieux :
Code : Java
1
2
3
4
5
6 |
String str = "Je-suis-un-ZérO";
String[] tab = str.split("-");//Donne un tableau à 4 entrées
//tab[0] vaut "Je"
//tab[1] vaut "suis"
//tab[2] vaut "un"
//tab[3] vaut "ZérO"
|
Voici
une façon de faire, un peu barbare mais elle fonctionne :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JFormattedTextField jtf;
private JFormattedTextField jtf2;
private JLabel label = new JLabel("Téléphone FR ");
private JButton b = new JButton ("OK");
/**
* Constructeur de l'objet
*/
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 150);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
try{
MaskFormatter tel = new MaskFormatter("**-**-**-**-**");
jtf = new JFormattedTextField(tel);
}catch(ParseException e){
e.printStackTrace();
}
JPanel top = new JPanel();
Font police = new Font("Arial", Font.BOLD, 14);
jtf.setFont(police);
jtf.setPreferredSize(new Dimension(150, 30));
jtf.setForeground(Color.BLUE);
b.addActionListener(new BoutonListener());
top.add(label);
top.add(jtf);
top.add(b);
this.setContentPane(top);
this.setVisible(true);
}
class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("Téléphone FR : " + jtf.getText());
String[] tab = jtf.getText().split("-");
//On contrôle le numéro de téléphone
//-------------------------------------
if(!controleData(tab)){
System.out.println("Numéro erroné ! ");
jtf.setText("");
}
else{
System.out.println("Numéro de téléphone OK ! ");
}
}
/**
* Méthode qui va contrôler la saisie
* @param data
* @return Boolean
*/
private boolean controleData(String[] data){
int i = 0;
//On balaye tout le contenu du tableau et on vérifie
//que les données sont conformes à nos attentes
while(i < data.length){
switch(i){
//Le premier élément doit être numérique et
//égal à 01 ou 02 ou 03 ou 04 ou 05 ou 06 ou 08 ou 09
case 0:
try {
int j = Integer.parseInt(data[i]);
if(j < 1 || j > 6 && j != 8 && j != 9)
return false;
} catch (NumberFormatException e) {
return false;
}
break;
//Les autres chiffres doivent être compris entre 00 et 99 INCLUS
//Je ne sais pas si ça marche réellement comme ça, mais c'est pour l'exemple...
case 1:
case 2:
case 3:
case 4:
try {
int j = Integer.parseInt(data[i]);
if(j < 0 || j > 99)
return false;
} catch (NumberFormatException e) {
return false;
}
break;
}
i++;
}
return true;
}
}
}
|
Ce qui nous donne :
Un
peu fastidieux comme façon de contrôler !
Imaginez un peu que vous ayez une
multitude de champs à vérifier... Une sacré galère au final !
Allez, dis-nous tout, on te connaît
maintenant...
Personnellement, je trouve qu'utiliser des expressions
régulières (ou
regex) permet plus
de souplesse et une économie de code assez conséquente.
On te croit sur parole ! Mais qu'est-ce qu'une regex
?
Bon, je n'ai pas l'intention de réinventer la poudre... Surtout que
M@teo a très bien expliqué ça dans deux chapitres de son tuto PHP. Vu que je
vais partir du principe que vous connaissez la base des expressions régulières,
je vous conseille vivement d'aller faire un tour sur
son
tuto et, une fois lu, revenez me voir ici-même...
Utiliser des expressions régulières
Comme vous avez pu le
constater lors de la lecture du tuto de M@teo, les regex permettent de faire
énormément de choses et, dans notre cas, de nous simplifier les contrôles de
saisie de notre
JFormattedTextField.
Maintenant,
afin de pouvoir contrôler la saisie, nous allons devoir définir la regex.
Comme mis dans les commentaires du code précédent, je pars du principe qu'un
numéro de téléphone est composé comme suit :
- un 0 en tout premier chiffre ;
- un chiffre qui peut être : 1, 2, 3, 4, 5, 6, 8, 9 ;
- ensuite, 4 blocs composés d'un "-" suivi d'un nombre compris entre 00 et
99 inclus.
Si vous avez bien suivi le tuto de M@teo sur les
regex, vous devez avoir une regex qui ressemble à ça :
#^0[0-689](-[0-9]{2}){2}$# ou à ça :
#^0[0-689](-[\d]{2}){2}$# Ces deux
regex sont tout à fait correctes pour une application PHP, mais elles ne
fonctionneront pas avec une application Java. Ceci pour deux raisons.
- En Java, il n'y a pas besoin de délimiteurs. Vous pouvez donc enlever les
deux "#". Ceci concerne les deux regex.
- Ce point, par contre, ne concerne que la deuxième. Le caractère
"\", est utilisé comme caractère d'échappement, ceci afin de
dé-spécialiser des caractères comme "\n", "\r"... La classe abrégée "\d", correspondant à un chiffre, ne
fonctionnera donc pas.
Afin de pouvoir utiliser les classes
abrégées dans une regex, il faut faire en sorte que le backslash de la classe
abrégée soit interprété comme tel et non comme un caractère d'échappement.
Comment ?

Il
faut tout simplement échapper le caractère d'échappement...
Ce qui nous donne
:
^0[0-689](-[\\d]{2}){2}$Le premier
backslash échappe le second, ce qui a pour conséquence que celui-ci est
interprété comme un backslash tout ce qu'il y a de plus normal et ainsi que
notre classe abrégée fonctionne !
Maintenant, nous sommes parés pour utiliser des regex...
Utiliser des regex
Avant de nous lancer tête baissée dans l'utilisation
des regex en Java, vous devez savoir que vous pouvez procéder de deux façon
différentes :
- en utilisant un objet String ;
- en utilisant l'API regex qui se trouve
dans le package java.util.regex .
Les regex et l'objet String
Vous allez
voir que c'est simplissime.
Nous avons donc convenu de la regex à utiliser
afin de contrôler nos saisies de numéros de téléphone.
Pour mémoire :
^0[0-689](-[\\d]{2}){4}$Il
ne nous reste plus qu'à dire au contenu de notre
JFormattedTextField
qu'il doit correspondre à celle-ci.
Cette
opération se fait grâce à la méthode
matches(String regex) , qui renvoie
true si notre chaîne
correspond à la regex ou
false, dans le cas contraire.
Voici
le code qui met en oeuvre cette démarche :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JFormattedTextField jtf;
private JFormattedTextField jtf2;
private JLabel label = new JLabel("Téléphone FR ");
private JButton b = new JButton ("OK");
/**
* Constructeur de l'objet
*/
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 150);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
try{
MaskFormatter tel = new MaskFormatter("**-**-**-**-**");
jtf = new JFormattedTextField(tel);
}catch(ParseException e){
e.printStackTrace();
}
JPanel top = new JPanel();
Font police = new Font("Arial", Font.BOLD, 14);
jtf.setFont(police);
jtf.setPreferredSize(new Dimension(150, 30));
jtf.setForeground(Color.BLUE);
b.addActionListener(new BoutonListener());
top.add(label);
top.add(jtf);
top.add(b);
this.setContentPane(top);
this.setVisible(true);
}
class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("Téléphone FR : " + jtf.getText());
if(jtf.getText().matches("^0[0-689](-[\\d]{2}){4}$")){
System.out.println("Numéro de téléphone OK ! !");
}
else{
System.out.println("Numéro de téléphone PAS OK ! !");
}
}
}
}
|
Ainsi que deux captures
d'écran afin de bien vous montrer le résultat :
Vous
pouvez voir que c'est très simple à utiliser...

Je
profite de cet aparté sur les regex afin d'introduire une autre méthode :
replaceAll(String regex, String remplacement)
.
Grâce à cette dernière, vous pourrez changer tous les caractères, ou
chaînes de caractères correspondant à la regex passée en premier paramètre par
la chaîne passée en deuxième paramètre.
Si nous appliquons ceci à notre
exemple, en partant du principe que, si la saisie du numéro de téléphone est
erronée, on remplace tous les caractères par des zéros, cela nous donne :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JFormattedTextField jtf;
private JFormattedTextField jtf2;
private JLabel label = new JLabel("Téléphone FR ");
private JButton b = new JButton ("OK");
/**
* Constructeur de l'objet
*/
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 150);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
try{
MaskFormatter tel = new MaskFormatter("**-**-**-**-**");
jtf = new JFormattedTextField(tel);
}catch(ParseException e){
e.printStackTrace();
}
JPanel top = new JPanel();
Font police = new Font("Arial", Font.BOLD, 14);
jtf.setFont(police);
jtf.setPreferredSize(new Dimension(150, 30));
jtf.setForeground(Color.BLUE);
b.addActionListener(new BoutonListener());
top.add(label);
top.add(jtf);
top.add(b);
this.setContentPane(top);
this.setVisible(true);
}
class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("Téléphone FR : " + jtf.getText());
if(jtf.getText().matches("^0[0-689](-[\\d]{2}){4}$")){
System.out.println("Numéro de téléphone OK ! !");
}
else{
System.out.println("Numéro de téléphone PAS OK ! !");
//Si la saisie est erronée
//On remplace tous les caractères alphabétiques par des 0
String str = jtf.getText().replaceAll("\\w", "0");
jtf.setText(str);
System.out.println("Après remplacement : " + str);
}
}
}
}
|
Et le résultat :
Je
pense que cette méthode pourrait vous être utile : on ne sait jamais...

Maintenant,
nous allons voir comment avoir le même résultat avec l'API regex.
Les regex et l'API regex
Avec cette méthode, nous allons
utiliser deux nouveaux objets :
- un objet Pattern
qui va contenir notre regex et qui va retourner un
objet contenant le résultat de la comparaison ;
- l'objet Matcher
qui est le résultat de la comparaison de la regex avec
la chaîne à tester.
Vous pourrez voir que ces objets sont très
simples à utiliser.
L'utilisation de l'objet
Pattern se fait comme
ceci :
Pattern pattern = Pattern.compile("^0[0-689](-[\\d]{2}){4}$");
Cette instruction déclare et initialise notre objet
Pattern, celui-ci est
maintenant prêt à tester des chaînes de caractères !
Le test d'une
chaîne par rapport à une regex via l'objet
Pattern se fait grâce à la méthode
matcher(String string) :
il ne s'agit pas de la regex en paramètre, mais de la chaîne à tester !

Comme
je vous l'ai dit plus haut, la comparaison via l'objet
Pattern renvoie un
objet
Matcher qui, lui, contient le résultat du
test (vrai ou faux) que nous pourrons récupérer grâce à la méthode
matches()
.
Voici un exemple simple :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13 |
String[] tab = {"abcdef", "16464","1v"};
//Regex qui vérifie si la chaîne ne contient que des chiffres
Pattern pattern = Pattern.compile("\\d+");
for(String str : tab){
Matcher matcher = pattern.matcher(str);
System.out.print("Teste sur '"+str+"' : ");
//On regarde le résultat
if(matcher.matches())
System.out.println("OK ! ! ");
else
System.out.println("PAS OK ! ! ");
}
|
Et voilà le résultat :
Rien
de plus simple, n'est-ce pas ?
On voit bien que le résultat est le même, mais...
l'intérêt ?
Je vais y venir, mais avant de vous expliquer pourquoi il
est intéressant de passer par l'objet
Pattern, vous devez savoir que vous pouvez
ne pas utiliser l'objet
Matcher.

Je
vois bien que vous êtes un peu dans le flou...
Reprenez ce que je vous ai
dit plus haut :
l'objet Pattern
retourne un objet Matcher.Par conséquent, vous pouvez
gagner un peu de mémoire en ne déclarant pas d'objet
Matcher mais en vous
servant de celui que vous retourne l'objet
Pattern !
Voilà le code précédent
mettant en oeuvre cette démarche :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12 |
String[] tab = {"abcdef", "16464","1v"};
//Regex qui vérifie si la chaîne ne contient que des chiffres
Pattern pattern = Pattern.compile("\\d+");
for(String str : tab){
System.out.print("Teste sur '"+str+"' : ");
//On regarde le résultat, et plus besoin d'instancier un objet Matcher
if(pattern.matcher(str).matches())
System.out.println("OK ! ! ");
else
System.out.println("PAS OK ! ! ");
}
|
Je ne vous mets pas de
capture d'écran car elle est identique à la précédente !
Tu ne voudrais pas nous expliquer ça ?
Bien
sûr...
En fait, repensez à la pile d'exécution lorsque nous avons abordé
les threads.
Ici, c'est la même chose. L'instruction
pattern.matcher(str).matches() se
découpe en deux.
Lors de l'exécution, la JVM va lire cette ligne, elle voit
qu'il y a plusieurs appels de méthode : par conséquent, elle va invoquer celle
qui doit être exécutée en premier, faire ce qu'elle a à faire, puis passer à la
suivante...
Voilà un schéma résumant la situation :
La
flèche indique le sens dans lequel la JVM va lire l'instruction et l'exécuter.
- Elle va lire le pattern.matcher(str) qui, comme je vous l'ai déjà
dit, retourne un objet Matcher. Étape 1.
- Ensuite, elle va exécuter la méthode matches() qui est une méthode de
l'objet Matcher. Étape 2.
Lors de
l'étape 2, c'est comme si vous aviez un objet
Matcher à la place de l'instruction
correspondant à l'étape 1... La méthode
matches() peut donc être invoquée !
Ainsi vous gagnez en objets, en lignes de codes et en mémoire...

Maintenant,
la réponse à la question que vous vous posez :
Pourquoi utiliser l'objet Pattern alors que l'objet String gère les regex
?
En fait, les deux méthodes sont
équivalentes...
C'est vrai que dans notre exemple, nous ne contrôlons qu'un
champ. Mais ce ne sera peut-être pas toujours le cas...
Imaginez-vous en
train de développer un progiciel de gestion avec, sur une de ses IHM, 35 champs
de saisie qui doivent contenir des codes spécifiques à une norme... La solution
des regex semble la plus optimisée mais
vous n'allez pas répéter la
regex pour tous les contrôles de tous les champs ! !Le jour où
votre chef va vous demander de mettre à jour ladite expression car un nouveau
code vient de faire son apparition, vous allez sûrement oublier un ou plusieurs
champs !

Le
fait d'utiliser un objet
Pattern, dans ce cas, permet de
centraliser la donnée qui va vous servir à contrôler vos champs et, au lieu de
faire X modifications, vous n'avez qu'à changer l'objet
Pattern.
Mais il y a une autre
alternative
Vous pouvez aussi stocker votre regex dans un
objet de type
String
et utiliser ce dernier dans tous vos contrôles, en
utilisant la méthode
matches(String regex) . Le but final étant de centraliser les données dont
vous vous servirez pour faire vos contrôles et que celles-ci soient facilement
modifiables sans risque d'oubli.
En bref, ces deux méthodes sont
équivalentes.
Je vous ai un peu induits en erreur, mais il était important
que vous connaissiez l'API regex.
Vous devez savoir tout de même que
lorsque vous utilisez la méthode
matches(String regex) de l'objet
String , celui-ci fait
appel à l'objet
Pattern
dans cette méthode...

De
même, lorsque vous utilisez la méthode
replaceAll(String regex, String remplacement) , celle-ci invoque
l'expression
Pattern.compile(regex).matcher(str).replaceAll(repl) .
Pour finir sur l'utilisation des
regex
Vous pouvez utiliser la
méthode qui vous convient, mais gardez en tête qu'il faut que vos contrôles
soient facilement modifiables !Bon, vous venez de voir
comment on peut gérer les saisies après les avoir tapées. Maintenant, je vous
propose de voir comment intercepter les saisies des utilisateurs avant que votre
composant ne soit affecté de quelque valeur que ce soit !
Contrôle du clavier : l'interface KeyListener
Tout est dans le titre de cette sous-partie !
Vous
connaissez déjà :
- l'interface MouseListener qui interagit avec votre
souris ;
- l'interface ActionListener qui interagit lors d'un
clic sur un composant ;
- l'interface ItemListener qui écoute les événements
sur une liste déroulante.
Voici à présent l'interface
KeyListener.
Comme dit dans le titre,
celle-ci va vous permettre d'intercepter les événements clavier lorsqu'on :
- presse une touche ;
- relâche une touche ;
- tape sur une touche.
Vous savez ce qu'il vous reste à
faire : créer un implémentation de cette interface dans notre projet.
Créez
une classe interne implémentant cette interface et utilisez l'astuce d'Eclipse
pour générer les méthodes à implémenter.
Vous constatez que celle-ci a
trois méthodes :
- KeyPressed(KeyEvent event) :
appelée lorsqu'on presse une touche ;
- keyReleased(KeyEvent event) :
appelée lorsqu'on relâche une touche. C'est à ce moment que le composant se
voit affecter la valeur de la touche ;
- keyTyped(KeyEvent event) :
appelée entre les deux méthodes citées ci-dessus.
Comme vous
devez vous en douter, l'objet
KeyEvent va nous permettre d'obtenir des
informations sur les touches qui ont été utilisées... Parmi celles-ci, nous
allons utiliser :
- getKeyCode() : retourne le
code de la touche ;
- getKeyChar() : retourne le
caractère correspondant à la touche.
Vous pouvez aussi savoir
si certaines touches de contrôle ont été utilisées (
SHIFT,
CTRL...), connaître le
composant à l'origine de l'événement... Nous n'en parlerons pas ici mais ce
genre d'informations sont faciles à trouver :
Google.
Pour des
raisons de simplicité, nous n'allons pas utiliser de
JFormattedTextField
mais un
JTextField sans
MaskFormatter. Ainsi,
nous n'aurons pas à nous préoccuper des "
-" de notre champ.
Pour commencer, nous allons
voir dans quel ordre se passent les événements clavier.
Voici le code source
que nous allons utiliser, il est presque identique aux précédents, rassurez-vous
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.text.ParseException;
import java.util.regex.Pattern;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.text.MaskFormatter;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JTextField jtf;
private JLabel label = new JLabel("Téléphone FR ");
private JButton b = new JButton ("OK");
//Création de l'objet pattern dont nous allons nous servir pour
//tester le contenu de notre champ
private Pattern regex;
/**
* Constructeur de l'objet
*/
public Fenetre(){
//On initialise notre pattern
this.regex = Pattern.compile("^0[0-689](-[\\d]{2}){4}$");
this.setTitle("Animation");
this.setSize(300, 150);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
jtf = new JTextField();
JPanel top = new JPanel();
Font police = new Font("Arial", Font.BOLD, 14);
jtf.setFont(police);
jtf.setPreferredSize(new Dimension(150, 30));
jtf.setForeground(Color.BLUE);
//On ajoute l'écouteur à notre composant
jtf.addKeyListener(new ClavierListener());
b.addActionListener(new BoutonListener());
top.add(label);
top.add(jtf);
top.add(b);
this.setContentPane(top);
this.setVisible(true);
}
class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("Téléphone FR : " + jtf.getText());
if(regex.matcher(jtf.getText()).matches()){
System.out.println("Numéro de téléphone OK ! !");
String str = jtf.getText().replaceAll("\\d", "X");
System.out.println("Après remplacement : " + str);
}
else{
System.out.println("Numéro de téléphone PAS OK ! !");
//Si la saisie est erronée
//On remplace tous les caractères alphabétiques par des 0
String str = jtf.getText().replaceAll("\\w", "0");
jtf.setText(str);
System.out.println("Après remplacement : " + str);
}
}
}
/**
* Implémentataion de l'interface KeyListener
* @author CHerby
*/
class ClavierListener implements KeyListener{
public void keyPressed(KeyEvent event) {
System.out.println("Code touche pressée : " + event.getKeyCode() +
" - caractère touche pressée : " + event.getKeyChar());
}
public void keyReleased(KeyEvent event) {
System.out.println("Code touche relâchée : " + event.getKeyCode() +
" - caractère touche relâchée : " + event.getKeyChar());
}
public void keyTyped(KeyEvent event) {
System.out.println("Code touche tapée : " + event.getKeyCode() +
" - caractère touche tapée : " + event.getKeyChar());
}
}
}
|
Voici un petit jeu d'essai
de ce code :
C'est
vrai que les événements vont tellement vite que n'avez pas le temps de voir le
défilement.
Vous pouvez ajouter une pause à la fin de chaque méthode de
l'implémentation afin de mieux voir l'ordre d'exécution, comme ceci :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 |
class ClavierListener implements KeyListener{
public void keyPressed(KeyEvent event) {
System.out.println("Code touche pressée : " + event.getKeyCode() +
" - caractère touche pressée : " + event.getKeyChar());
pause();
}
public void keyReleased(KeyEvent event) {
System.out.println("Code touche relâchée : " + event.getKeyCode() +
" - caractère touche relâchée : " + event.getKeyChar());
pause();
}
public void keyTyped(KeyEvent event) {
System.out.println("Code touche tapée : " + event.getKeyCode() +
" - caractère touche tapée : " + event.getKeyChar());
pause();
}
}
private void pause(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
|
Maintenant,
vous pouvez voir dans quel ordre les événements du clavier sont gérés.
En
premier lorsqu'on presse la touche, en second lorsque celle-ci est tapée et
enfin relâchée.
Dans le cas qui nous intéresse, nous voulons que lorsque
l'utilisateur saisit un caractère non autorisé, celui-ci soit retiré
automatiquement de la zone de saisie. Pour cela, nous allons faire un traitement
spécifique dans la méthode
keyReleased(KeyEvent event).
Si vous avez fait
beaucoup de tests de touches, vous avez dû remarquer que les codes des touches
correspondant aux chiffres du pavé numérique sont compris entre
96 et
105
!
À partir de là, c'est simple : il vous suffit de supprimer le caractère
tapé de la zone de saisie si le code de celui-ci n'est pas compris dans cette
tranche de code.
Mais voilà, un problème se pose avec cette méthode : pour
celles et ceux qui ont un PC portable, sans pavé numérique, la saisie sera
impossible alors que vous pouvez avoir des chiffres en faisant
MAJ + (
& ou
é ou
' ou
( ou encore
- ...).
À cause de ce souci, nous allons opter pour une autre méthode. Nous
allons créer une méthode ayant comme type de renvoi un booléen et qui va se
charger de nous dire si la saisie est numérique ou non. Comment ? Tout
simplement en faisant un
Integer.parseInt(value); , le
tout enveloppé dans un
try{...}
catch(NumberFormatException ex){}.
Si nous essayons
de convertir un caractère "a" en entier, l'exception sera levée et nous
retournerons FAUX, et VRAI dans le cas contraire...
Attention : la méthode parseInt() prendra un String en paramètre ! La méthode getKeyChar(), elle, nous renvoie un char... Il faudra penser à faire la
conversion...
Voici notre implémentation quelque peu modifiée :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 |
class ClavierListener implements KeyListener{
public void keyReleased(KeyEvent event) {
if(!isNumeric(event.getKeyChar())){
jtf.setText(jtf.getText().replace(String.valueOf(event.getKeyChar()), ""));
}
}
//Inutile de redéfinir ces méthodes
//Nous n'en avons plus l'utilité !
public void keyPressed(KeyEvent event) {}
public void keyTyped(KeyEvent event) {}
/**
* Retourne vrai si le paramètre est numérique
* Retourne Faux dans le cas contraire
* @param carac
* @return Boolean
*/
private boolean isNumeric(char carac){
try {
Integer.parseInt(String.valueOf(carac));
} catch (NumberFormatException e) {
return false;
}
return true;
}
}
|
Vous pouvez voir que les
lettres simples sont désormais interdites à la saisie =>
Mission
accomplie !
Les caractères spéciaux comme "ô", "ï"... ne sont pas
pris en charge par cette méthode... Par conséquent, leur saisie reste possible

. Mais c'est à ça que sert notre contrôle avec la regex

.
Par contre, je ne sais pas pour vous mais, le fait d'avoir deux
méthodes sans corps me dérange un peu...
On peut éviter ce genre de chose ?

Comment
? Puisque nous devons redéfinir toutes les méthodes de l'interface
!
Tout à fait. Il existe une classe,
KeyAdapter, que vous pouvez étendre (par
là comprenez : créez une classe héritée) et ne redéfinir que la méthode qui nous
intéresse, et donc ADIEU aux deux méthodes vides !
Vous pouvez bien
entendu créer un classe interne héritée de
KeyAdapter et redéfinir la méthode
keyReleased(KeyEvent
event) mais je vais en profiter pour vous montrer une
autre méthode.
Utiliser les classes anonymes
Il n'y a rien de compliqué dans
cette manière de faire, mais je me rappelle avoir été déconcerté lorsque je l'ai
vue pour la première fois...
En fait, les classes anonymes sont le plus
souvent utilisées pour la gestion d'événements ponctuels, là où créer une classe
pour un seul traitement est trop lourd...
Notre exemple est très bien
pour les classes anonymes : nous n'avons qu'un champ et une redéfinition de
méthode. Maintenant, adieu à l'implémentation que vous avez codée tout à
l'heure, nous allons dire à notre
JTextField qu'une instance d'une classe
anonyme va l'écouter. Attention les yeux, ça risque de piquer un peu :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.regex.Pattern;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JTextField jtf;
private JLabel label = new JLabel("Téléphone FR ");
private JButton b = new JButton ("OK");
//Création de l'objet pattern dont nous allons nous servir pour
//tester le contenu de notre champ
private Pattern regex;
/**
* Constructeur de l'objet
*/
public Fenetre(){
//On initialise notre pattern
this.regex = Pattern.compile("^0[0-689](-[\\d]{2}){4}$");
this.setTitle("Animation");
this.setSize(300, 150);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
jtf = new JTextField();
JPanel top = new JPanel();
Font police = new Font("Arial", Font.BOLD, 14);
jtf.setFont(police);
jtf.setPreferredSize(new Dimension(150, 30));
jtf.setForeground(Color.BLUE);
//**********************************************
//Voilà notre classe anonyme
//**********************************************
jtf.addKeyListener(new KeyAdapter(){
public void keyReleased(KeyEvent event) {
System.out.println("keyReleased dans une classe anonyme");
if(!isNumeric(event.getKeyChar())){
jtf.setText(jtf.getText().replace(String.valueOf(event.getKeyChar()), ""));
}
}
private boolean isNumeric(char carac){
try {
int i =Integer.parseInt(String.valueOf(carac));
} catch (NumberFormatException e) {
return false;
}
return true;
}
});
//**********************************************
b.addActionListener(new BoutonListener());
top.add(label);
top.add(jtf);
top.add(b);
this.setContentPane(top);
this.setVisible(true);
}
class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("Téléphone FR : " + jtf.getText());
if(regex.matcher(jtf.getText()).matches()){
System.out.println("Numéro de téléphone OK ! !");
String str = jtf.getText().replaceAll("\\d", "X");
System.out.println("Après remplacement : " + str);
}
else{
System.out.println("Numéro de téléphone PAS OK ! !");
//Si la saisie est erronée
//On remplace tous les caractères alphabétiques par des 0
String str = jtf.getText().replaceAll("\\w", "0");
jtf.setText(str);
System.out.println("Après remplacement : " + str);
}
}
}
}
|
Ce code a le même effet que
le précédent : la seule chose qui change, c'est qu'au lieu d'avoir une
implémentation de l'interface
KeyListener ou d'avoir une classe interne
héritée de
KeyAdapter, nous utilisons une classe
anonyme au moment où nous définissons l'écouteur pour notre
composant.
Décortiquons tout ça...
Nous avons toujours notre
instruction
jtf.addKeyListener();
, sauf qu'au lieu de lui donner une instance habituelle, nous créons une
classe qui redéfinit la méthode qui nous intéresse. Ceci en faisant
:
Code : Java
1
2
3 |
new KeyAdapter(){
//Redéfinition de la classe
};
|
De ce fait, vous pouvez
aussi créer une instance de type
KeyAdapter en utilisant une classe interne
comme implémentation :
Code : Java
1
2
3
4 |
KeyAdapter kAdapter = new KeyAdapter(){
//Redéfinissions de la classe
};
jtf.addKeyListener(kAdapter);
|
Attention : vous avez dû le remarquer
mais je préfère le dire, dans ce type de déclaration, le ";" final se trouve après l'accolade fermante
de la classe anonyme ! !
L'une des particularités de cette
façon de faire, c'est que cet écouteur n'écoutera que ce composant !
Pourquoi on appelle ça une classe anonyme ?
C'est simple : le fait de
procéder de cette manière revient à créer une classe héritée sans être obligés
de créer de façon explicite ladite classe.
L'héritage se fait
automatiquement, en fait, le code ci-dessus reviendrait à faire :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 |
class Fenetre extends JFrame{
//...
jtf.addKeyListener(new KeyAdapterBis());
//...
public class keyAdapterBis extends Keyadapter{
public void keyReleased(KeyEvent event) {
System.out.println("keyReleased dans une classe anonyme");
if(!isNumeric(event.getKeyChar())){
jtf.setText(jtf.getText().replace(String.valueOf(event.getKeyChar()), ""));
}
}
private boolean isNumeric(char carac){
try {
int i =Integer.parseInt(String.valueOf(carac));
} catch (NumberFormatException e) {
return false;
}
return true;
}
}
}
|
Mais la classe créée n'a pas
de nom ! L'héritage se fait de façon tacite. On bénéficie donc de tous les
avantages de la classe mère en ne redéfinissant que la méthode qui nous
intéresse.

Vous
devez savoir aussi que les classes anonymes peuvent être utilisées pour
implémenter des interfaces. Ce code est tout aussi équivalent aux précédents :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 |
jtf.addKeyListener(new KeyListener(){
public void keyReleased(KeyEvent event) {
System.out.println("keyReleased dans une classe anonyme");
if(!isNumeric(event.getKeyChar())){
jtf.setText(jtf.getText().replace(String.valueOf(event.getKeyChar()), ""));
}
}
private boolean isNumeric(char carac){
try {
int i =Integer.parseInt(String.valueOf(carac));
} catch (NumberFormatException e) {
return false;
}
return true;
}
//Méthode de l'interface a redéfinir
public void keyPressed(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
});
|
Les classes anonymes sont
soumises aux mêmes lois que les classes
normales :
- utilisation des méthodes non redéfinies de la classe mère ;
- OBLIGATION de redéfinir TOUTES LES MÉTHODES d'une interface ;
- OBLIGATION de redéfinir les méthodes abstraites d'une classe abstraite.
Cependant, elles ont des restrictions de par leur essence et
par là, je veux dire leur rôle et leur but :
- ces classes ne peuvent pas être déclarées abstract !
- elles ne peuvent pas non plus être static ;
- elles ne peuvent pas définir de constructeur ;
- elles sont automatiquement déclarées final : impossible de dériver de cette
classe, donc héritage impossible !
Encore une chose avant de
terminer ce chapitre sur le JTextField : il existe encore deux objets
fonctionnant de la même manière :
- le JPasswordField
: utilisé pour les saisies de mots de passe ;
- le JTextArea
: utilisé, lui, pour les saisies multilignes.
Essayez-les, vous verrez que leur utilisation est très
simple.
Bon, après toutes ces émotions, je crois qu'un petit topo
s'impose...
Ce qu'il faut retenir
- Par défaut, un JTextField
accepte tous types de caractères.
- Un JFormattedTextField
est, pour simplifier, un JTextField plus
restrictif.
- On peut restreindre la saisie d'un de ces objets en utilisant l'objet
MaskFormatter.
- Pour contrôler les événements clavier, l'utilisation d'une implémentation
de l'interface KeyListener est de mise.
- Vous pouvez utiliser une classe dérivée de KeyAdapter à la place
d'une implémentation de KeyListener.
- Une classe anonyme est propre à un objet.
- Une classe anonyme est automatiquement déclarée final : donc pas
d'héritage possible.
- Vous pouvez utiliser les regex avec l'objet String ou avec
l'objet Pattern.
Vous avez vu que cet objet est très simple à
utiliser...
La plupart des composants que vous pouvez utiliser dans un
formulaire de base ont été vus.
Je vous propose donc de faire un détour vers
ce qu'on appelle les Applets.
Les applets
Dans ce chapitre, vous apprendrez tout ce qu'il y a savoir
sur les applets :
- ce qu'elles sont ;
- comment les créer ;
- comment les intégrer dans une page web ;
- faire interagir votre applet via du Javascript ;
- appeler du Javascript via votre applet ;
- ...
Par contre, afin de suivre ce chapitre dans les
meilleures conditions, je vous conseille d'avoir des bases en
Javascript.
Pour ce faire, vous pouvez utiliser
ce
tuto.
Vous pouvez le lire jusqu'au chapitre 2 de la partie 2, au moins !
Je sens que vous êtes impatients de commencer, alors allons-y !
Les applets : quésaco ?
Une applet, oui, on dit
une
applet, est une application Java insérée dans une page web.
Ce genre d'application est appelée
application
client car elle est utilisée par celui qui appelle la page web (le
client), et non par celui qui la génère (le serveur).
Il ne faut pas
confondre une applet et une application
J2EE (maintenant
JEE). Pour bien comprendre à quel
niveau se situe l'applet dans le cycle de vie d'une page web, un petit schéma
s'impose. Ce schéma représente le cycle de vie d'une page web dite dynamique,
c'est-à-dire que celle-ci contient du code interprété par le serveur (Java, PHP,
ASP...) qui est ici une page contenant du code Java :

- (1) Le client émet
une requête (saisie d'une URL, clic sur un lien...) pour demander une
ressource au serveur. Exemple : http://www.monserveur.com/tuto.do.
Il ne sait pas ici si la réponse qui va lui parvenir est statique (page HTML
simple) ou dynamique (générée par une application web). Dans notre cas, il
s'agit d'une application répondant à l'adresse "tuto.do" sur le
serveur "monserveur.com".
- (2) Côté serveur,
c'est le serveur web (exemple : Apache) qui traite les requêtes
HTTP entrantes. Il traite donc toutes les requêtes, qu'elles
demandent une ressource statique ou dynamique. Seulement, un serveur HTTP ne
sait répondre qu'aux requêtes visant des ressources statiques. Il ne
peut que renvoyer des pages HTML, des images, des applets...
existantes.
- (3) Ainsi, si le serveur HTTP
s'aperçoit que la requête reçue est destinée au serveur d'applications, il la
lui transmet. Les deux serveurs sont reliés par un canal, nommé "connecteur".
- (4) Le serveur
d'applications (exemple : Tomcat ! Serveur d'applications Java) reçoit la
requête à son tour. Il est, lui, en mesure de la traiter. Il exécute
donc le morceau d'application (la servlet) auquel
est destinée la requête, en fonction de l'URL. Cette opération est effectuée à
partir de la configuration du serveur. La servlet est donc invoquée, et le
serveur lui fournit notamment deux objets Java exploitables : un représentant
la requête, l'autre représentant la réponse. La servlet peut
maintenant travailler, et générer la réponse à la demande. Cela peut
passer par la consultation de sources de données, comme des bases de données
(4'' sur le schéma). Ou bien par l'interrogation d'autres serveurs ou systèmes
(4' sur le schéma), l'environnement Java web permettant de se connecter à de
nombreux systèmes.
- (5) Une fois sa
réponse générée, le serveur d'applications la renvoie, par le connecteur, au
serveur web. Celui-ci la récupère comme s'il était lui-même allé
chercher une ressource statique. Il a simplement délégué la récupération de la
réponse, et celle-ci a été générée, mais ce n'est plus le problème.
- (6) La réponse est
dorénavant du simple code HTML, compréhensible par un navigateur. Le
serveur HTTP peut donc retourner la réponse au client. Celle-ci contient
toutes les ressources nécessaires (feuilles Javascript, feuilles CSS, applet
Java, images...).
Ceci
est un résumé du cycle de vie d'une page web faite avec la technologie J2EE. Je
me doute que vous devez avoir des sueurs froides, mais ne vous inquiétez pas,
nous reverrons tout ça plus en détail lorsque nous aborderons le développement
web en Java...
Pour le moment, tout ce que vous avez besoin de savoir c'est
qu'
une applet est une ressource utilisée par votre navigateur, tout
comme une image : à la différence que là, il s'agit d'un programme qui va
s'exécuter sur votre page ! 
Les
ressources utilisées par votre navigateur pour charger et utiliser une applet
sont chargées au chargement de la page, après que le serveur web ait renvoyé la
réponse à votre requête.
Ces ressources sont dans le code source HTML
de la page et le navigateur charge tout ce dont il a besoin pour
afficher la page comme le développeur l'a souhaité (images, feuilles CSS,
applet...).
Vous pouvez voir un aperçu de notre animation version
applet
par
ici.
Vous constaterez que le programme fonctionne parfaitement, comme
s'il s'agissait d'une application fenêtrée, mais là, notre programme Java
s'exécute sur une page web !
Attention : il se
peut que votre navigateur n'autorise pas l'exécution des applets Java
!
Pour remédier à ce problème, vous devez allez dans les options
internet : menu Outils > Options
dans l'onglet Contenu : cochez "autoriser le Java", sous Firefox.
Sous IE 7,
faites : Outils > Options
internet, dans l'onglet "content", cochez la case "utiliser JRE 1.X.XX pour les applets" où X.XX
correspond à la version de votre JRE installé sur votre
machine.
Maintenant, vous savez distinguer une application client
d'une application serveur et donc, vous ne devrez plus faire l'amalgame entre
applet et servlet !
Votre première applet
Il est temps maintenant de faire votre première
applet.
Vous allez voir que c'est très simple et que ça ressemble beaucoup à
ce que vous avez fait jusque-là. En fait, c'est quasiment identique à une
exception près :
une applet n'a pas de constructeur mais elle utilise
la méthode init() de la super-classe Applet du package awt
ou JApplet du package
swing.
Codage de l'applet
Nous allons faire une applet avec un code
minimal, disons un label et un bouton. Lors du clic sur
bouton, nous afficherons le nombre de clics effectués.
Rien de bien méchant. Créez un nouveau projet avec une classe
FirstApplet héritée de
JApplet.
Voici le code source de
votre première applet :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
public class FirstApplet extends JApplet {
private JLabel label = new JLabel();
private JButton bouton = new JButton("Cliquez");
private int count = 0;
/**
* Méthode d'initialisation de l'applet
* C'est cette méthode qui fait office de constructeur
*/
public void init(){
this.setSize(300, 80);
//On centre le texte du JLabel et on écrit en bleu...
label.setHorizontalAlignment(JLabel.CENTER);
//C'est plus zoli.
label.setForeground(Color.blue);
//Allez, une classe anonyme... Just for the fun ;)
this.bouton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
label.setText("Vous avez cliqué " + (++count) + " fois sur le bouton");
}
});
//On ajoute nos composants
this.getContentPane().add(bouton, BorderLayout.SOUTH);
this.getContentPane().add(label, BorderLayout.NORTH);
//Et le tour est joué !
}
}
|
Avant de vous lancer dans le
test de cette première applet, vous devez savoir tout de même que, mis à part
quelques méthodes comme
setTitle("Animation") ,
setLocationRelativeTo(null) et
quelques autres propres aux objets
JFrame, les applets s'utilisent de la même
manière. Bien sûr, avec la méthode
public void init() à la place d'un constructeur !
Maintenant, avant
d'intégrer votre programme dans une page web, vous devez vous assurer que
celui-ci fonctionne correctement.
Comment on fait ça ? Tu nous a dit que les applets
étaient utilisées dans des pages web...
Je maintiens, mais Eclipse vous
propose un moyen d'exécuter votre classe comme une applet.

Pour
faire cela, faites un clic droit sur votre fichier puis choisissez "
Run as" et enfin choisissez "
Java Applet", comme ceci :
Vous
pouvez voir, ébahis, le résultat de votre applet :
Vous avez un indicateur vous signalant si votre
applet est lancée ou non. Si vous voyez le message : "Applet not inited", ça veut dire qu'une erreur
s'est glissée dans votre code et que la JVM n'a pas pu initialiser l'applet
!
Insertion dans une page HTML
Pour que votre navigateur sache que
la ressource à utiliser est une applet Java, vous devez utiliser la balise HTML
<applet></applet>
.
Celle-ci peut prendre plusieurs attributs et vous pouvez même passer
des paramètres à votre applet grâce à cette balise.
Voici la liste des
paramètres que peut prendre la balise
<applet></applet> :
- name="FirstAnimation" : nom de l'applet,
ici FirstAnimation . Nous allons vite voir
l'intérêt de cet attribut ;
- width="300px" : largeur de l'applet
affichée, ici, 300 pixels ;
- height="300px" : hauteur de l'applet
affichée ;
- codebase="class/" : l'URL de base pour
l'applet, c'est-à-dire l'endroit où le navigateur peut trouver les fichiers
.class ; ici, les fichiers sont dans le dossier class à côté de votre fichier HTML ;
- code="FirstAnimation.class" : fichier de
classe de l'applet. Celui où se trouve la méthode init() ;
- archive="plugin.jar" : identifie les
ressources à pré-charger (.jar, images...) ; ici, nous pré-chargeons une
archive Java appelée plugin.jar ;
- alt="Please Wait..." : affiche un texte au
cours du chargement ;
- hspace="10px" : espacement horizontal
entre l'applet et un autre contenu (div ou
autre bloc HTML...) ;
- vspace="20px" : idem que précédemment mais
pour l'espacement vertical.
Pfiou... Il y en a des attributs
pour cette balise...
Voici donc un exemple de balise applet :
Code :
HTML
1
2
3
4 |
<applet name="FirstAnimation" codebase="class/" code="FirstAnimation.class"
height="300px" width="300px" archive="plugin.jar">
<param name="message" value="Message pour les ZérOs">
</applet>
|
Heu...
On comprend bien tout mais, qu'est-ce que c'est que ce truc :
<param name="message" value="Message pour les ZérOs"> ?
Je vous ai dit que vous pouviez passer
des paramètres à votre applet. Eh bien c'est comme ceci que nous allons nous y
prendre !

Ceci
veut dire que nous pourrons utiliser la méthode
getParameter(String
paramName); qui va nous
renvoyer un
String correspondant à l'attribut
value de la balise. Ici, on aurait
this.getParameter("message")//Retourne : Message pour les ZérOs.
Tenez,
nous allons essayer ça ! Ajoutez ceci dans votre méthode
init() :
System.out.println("Paramètre passé via la balise <param> :
" + this.getParameter("message")); .
Lancez votre
applet et :
Ah
oui ! Si vous ne spécifiez pas de paramètre pour votre applet, ledit paramètre
vaut
null.
Voici comment on
procède pour spécifier un paramètre pour votre application. Déjà, faites un clic
droit sur votre fichier puis allez dans
Propriétés.
Ensuite, cliquez sur "
Run/Debug settings" puis sur le fichier
correspondant à votre applet et enfin sur "
Edit", comme ceci :
Dans
la fenêtre dans laquelle vous êtes maintenant, choisissez l'onglet
"
parameter".
Vous
arrivez enfin à l'endroit où vous pouvez ajouter des paramètres. Cliquez sur
"
Add", comme ceci :
Et
enfin, renseignez le nom de votre paramètre ainsi que sa valeur :
Cliquez
sur "
Ok" sur toutes les fenêtres que
vous avez ouvertes et relancez votre applet, le paramètre s'affiche enfin !
Vous
pouvez maintenant incorporer votre applet dans une page HTML.
Voici le code
de ladite page :
Code : HTML
1
2
3
4
5
6
7
8
9
10
11
12 |
<html>
<body style="margin:auto;">
<div style="width:320px;margin:auto;margin-top:100px;border:5px solid black">
<applet codebase="class/" code="FirstApplet.class" height="80" width="300" hspace="10" vspace="10">
<param name="message" value="Message pour les ZérOs">
</applet>
</div>
</body>
</html>
|
J'ai créé ce code et
enregistré le fichier contenant ce code HTML sur mon bureau, j'ai donc dû
déplacer mes fichiers .class - oui, vous ne rêvez pas, j'ai dit :
mes
fichiers .class - dans un dossier, que j'ai appelé "
class" pour l'occasion (cf. paramètre codebase de
l'applet)...
J'ai récupéré mes fichiers .class dans le répertoire
/bin de votre projet, et vous
pouvez voir que vous avez
FirstApplet.class et
FirstApplet$1.class dans le cas où vous avez exactement le même
code que moi.
À quoi ça correspond ?
En fait,
FirstApplet.class correspond à la compilation de votre classe
FirstApplet et
FirstApplet$1.class correspond à la compilation de votre classe
anonyme ! Pas de nom pour cette classe, donc la JVM remplace le nom par
"
$1". Si vous aviez utilisé une
classe interne, appelons-la
BoutonListener et si
vous compiliez le code, vous auriez toujours
FirstApplet.class,
mais vous auriez eu le fichier
FirstApplet$BoutonListener.class.
Donc, si vous avez
créé votre page web ailleurs que dans votre dossier contenant votre projet, vous
devrez déplacer tous les fichiers .class commençant par
FirstApplet et toutes les autres ressources que
votre applet utilise (images, autres classes, archives Java...).
Maintenant
que toute la lumière est faite sur ce point, vous pouvez aller voir votre
première applet :
ici.
Il faut que vous sachiez que, si Eclipse est assez
laxiste pour lancer l'applet même si le paramètre "message" n'est pas renseigné, votre navigateur,
enfin la JVM de votre navigateur sera moins conciliante... Si le
paramètre est manquant, l'applet plantera !
Voilà, vous
venez de faire votre première applet ! Alors, heureux ?
Nota Bene
Avant de continuer, vous devez savoir une dernière
chose, ceci ne concerne pas directement Java mais la balise
<applet></applet>
.
En fait, depuis la sortie de HTML 4.0, la balise
<applet></applet>
est dépréciée par le
W3C, c'est-à-dire que cet organisme
préconise l'utilisation de la balise
<object></object> .
Ceci en grande
partie à cause de
IE qui gérait le
Java avec sa propre JVM (version 1.1, c'est vieux...) et non celle de Sun
Microsystems (bientôt 1.7...). Il faut, afin que la balise
<applet></applet>
fonctionne correctement sous IE, avoir installé un environnement Java et
s'assurer que IE utilise celui-ci pour interpréter du Java... (cf. plus
haut).
Je ne détaillerai pas l'utilisation de cette balise vu que Sun
Microsystems recommande l'utilisation de la balise
<applet></applet>
... Voici tout de même un exemple d'utilisation de la balise
<object></object>
:
Code : HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
<!-- L'Utilisation des commentaires conditionnels propres à IE sont à utiliser -->
<!-- car même si IE requiert l'utilisation de cette balise, il ne l'interprète pas comme les autres -->
<!--[if IE]>
<object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" width="300" height="80" name="CryptoApplet">
<param name="java_code" value="FirstApplet.class" />
<param name="java_codebase" value="class/" />
<param name="java_archive" value="plugin.jar" />
<param name="type" value="application/x-java-applet;version=1.5" />
<![endif]-->
<!-- Ceci est pour les autres navigateurs, IE ignorera cette instruction ! -->
<!--[if !IE]>
<object classid="FirstApplet.class" type="application/x-java-applet;version=1.5" archive="plugin.jar" height="80" width="300">
<param name="message" value="Message pour les ZérOs" />
</object>
<![endif]-->
|
Il est grand temps de voir
comment notre page web peut interagir avec notre applet...

Go
!
Interaction page web - applet
C'est à ce moment précis que vous aurez besoin des
bases de Javascript.
Eh oui, la communication page web - applet se fait via
un script Javascript !

En
tout premier lieu, nous allons créer une méthode qui fait exactement la même
chose que l'action lors du clic sur le bouton de notre applet, mais qui n'est
jamais appelée dans notre applet...
C'est cette méthode que nous allons
utiliser via Javascript :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
public class FirstApplet extends JApplet {
private JLabel label = new JLabel();
private JButton bouton = new JButton("Cliquez");
private int count = 0;
/**
* Méthode d'initialisation de l'applet
* C'est cette méthode qui fait office de constructeur
*/
public void init(){
this.setSize(300, 80);
System.out.println("Paramètre passé via la balise <param> : " + this.getParameter("message"));
//On centre le texte du JLabel et on écrit en bleu...
label.setHorizontalAlignment(JLabel.CENTER);
//C'est plus zoli.
label.setForeground(Color.blue);
//Allez, une classe anonyme... Just for the fun ;)
this.bouton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
label.setText("Vous avez cliqué " + (++count) + " fois sur le bouton");
}
});
//On ajoute nos composants
this.getContentPane().add(bouton, BorderLayout.SOUTH);
this.getContentPane().add(label, BorderLayout.NORTH);
//Et le tour est joué !
}
/**
* Méthode utilisée par Javascript pour mettre à jour
* Celle-ci doit être public afin que Javascript puisse y avoir accès
* Le JLabel
*/
public void doJavascript(){
System.out.println("Méthode appelée par javascript ! ");
label.setText("Vous avez cliqué " + (++count) + " fois sur le bouton");
}
}
|
Rien de bien sorcier pour
l'instant... Maintenant, nous allons voir comment appeler une méthode d'une
applet Java dans un script Javascript. Pour ce faire, nous allons rajouter un
simple bouton HTML sur notre applet précédente avec, lors du clic sur le bouton,
l'appel à la fonction de l'applet.
Voici le code source HTML de cette
page :
Code : HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 |
<html>
<body style="margin:auto;">
<div style="width:100%;text-align:center;background-color:#aef15b;"> </div>
<div style="width:100%;text-align:center;background-color:#aef15b;">
<input type="button" value="Lancer la méthode de l'applet" onClick="document.applets['first'].doJavascript();"/>
</div>
<div style="width:100%;text-align:center;background-color:#aef15b;"> </div>
<div style="width:320px;margin:auto;margin-top:20px;border:5px solid black">
<applet codebase="class/" code="FirstApplet.class" height="80" width="300" hspace="10" vspace="10" name="first" id="firstApplet">
<param name="message" value="Message pour les ZérOs">
</applet>
</div>
</body>
</html>
|
Vous pouvez voir le résultat
:
ici.
Voyons
un peu comment cela fonctionne.
Votre navigateur charge l'applet.
Vous
avez spécifié que lorsque nous cliquerons sur le bouton (événement onClick) nous
exécuterons un morceau de code Javascript :
document.applets['first'].doJavascript(); .
À l'appel de
cette instruction, le script se positionne sur l'applet portant le nom
first (notez que vous pouvez aussi
utiliser un index, commençant par 0, pour utiliser des applets via Javascript :
ici,
document.applets[0].doJavascript() est équivalent) sur notre document et appelle la
méthode
doJavascript() déclarée dans cette applet. Celle-ci met à
jour le
JLabel à l'intérieur de l'applet, et le
tour est joué !
Nous
allons aborder maintenant un point intéressant, la communication applet - page
web !
Interaction applet - page web
Dans ce cas, la communication se fait dans l'autre
sens, c'est-à-dire que c'est votre applet qui va mettre à jour votre page web
!
Ceci se fait toujours grâce à du Javascript, sauf que maintenant, c'est
notre applet qui va invoquer une méthode Javascript...

Pour
réussir ce tour de force, nous allons avoir besoin d'un objet particulier, un
JSObject,
non présent d'office dans les ressources disponibles Java. Nous allons donc
devoir utiliser une bibliothèque externe, l'ajouter à notre projet, recompiler
le programme et ajouter cette nouvelle ressource dans la déclaration de notre
applet dans la page web.
Où peut-on trouver cet objet, alors ?
Il est
dans votre JRE, et là, je veux dire dans le dossier
/lib de
votre JRE.
L'objet en question se trouve dans l'archive plugin.jar qui se
trouve, je le répète, dans le dossier
/lib de votre JRE (ou
JDK).
Copiez cette archive dans le dossier contenant votre projet et
mettez-le dans un dossier
/lib que vous créerez. Vous devez
maintenant avoir trois dossiers dans votre projet :
- /src : contient les fichiers sources de votre applet ;
- /bin : contient les fichiers .class de votre applet ;
- /lib : contient l'archive plugin.jar.
Maintenant, nous allons dire à Eclipse qu'il peut utiliser la
nouvelle ressource afin que nous puissions utiliser les classes qu'elle
contient.
Pour ce faire, faites un clic droit sur votre projet, puis dans
"
Build Path" choisissez "
Add External Archives" comme ceci :
Allez
maintenant dans le dossier
/lib de votre projet et double
cliquez sur
plugin.jar. Vous devez voir que, maintenant, la
ressource externe fait partie de votre projet : vous pouvez utiliser les classes
que contient l'archive
plugin.jar, et même voir son contenu :
L'objet
tant convoité se trouve dans le package
netscape.javascript. Oui, vous ne rêvez
pas et il s'agit bien du même netscape que vous connaissez : le navigateur web !
Ce sont eux qui ont développé ces classes...

Bon,
nous sommes parés à utiliser l'objet
JSObject mais avant, nous allons mettre à
jour notre page web afin que notre applet puisse écrire quelque
part...
Nous allons donc retirer notre bouton HTML pour le remplacer par
un
div vide. C'est dans ce dernier que notre
applet va écrire via du Javascript.
Il nous faut aussi une méthode Javascript
pour écrire dans notre
div : rien de
difficile... Alors, qu'attendons-nous ?

Voici
le code de notre page web :
Code : HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 |
<html>
<body style="margin:auto;">
<script type="text/javascript">
function affiche(str){
document.getElementById('label').innerHTML = str;
}
</script>
<div style="width:100%;text-align:center;background-color:#aef15b;"> </div>
<div style="width:100%;text-align:center;background-color:#aef15b;color:white;font-weight:bold;font-size:1.2em;" id="label"> </div>
<div style="width:100%;text-align:center;background-color:#aef15b;"> </div>
<div style="width:320px;margin:auto;margin-top:20px;border:5px solid black">
<applet mayscript="true" codebase="class3/" archive="lib/plugin.jar" code="FirstApplet.class" height="80" width="300" hspace="10" vspace="10" name="first">
<param name="message" value="Message pour les ZérOs">
</applet>
</div>
</body>
</html>
|
N'oubliez surtout pas l'attribut mayscript dans votre applet : sans celui-ci,
votre applet ne sera pas habilitée à utiliser l'objet JSObject !
Et voici le code de votre applet :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
import netscape.javascript.JSException;
import netscape.javascript.JSObject;
public class FirstApplet extends JApplet {
private JLabel label = new JLabel();
private JButton bouton = new JButton("Cliquez");
private int count = 0;
private JSObject jso;
/**
* Méthode d'initialisation de l'applet
* C'est cette méthode qui fait office de constructeur
*/
public void init(){
setSize(300, 80);
//On initialise l'objet
try {
jso = JSObject.getWindow(this);
} catch (JSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Paramètre passé via la balise <param> : " + this.getParameter("message"));
//On centre le texte du JLabel et on écrit en bleu...
label.setHorizontalAlignment(JLabel.CENTER);
//C'est plus zoli.
label.setForeground(Color.blue);
//allez une classe anonyme... Just for the fun ;)
this.bouton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
String str = "Vous avez cliqué " + (++count) + " fois sur le bouton";
label.setText(str);
//On appelle la fonction Javascript
//ceci peut lever une exception
try {
//On appelle la méthode affiche en lui passant en paramètre
//un tableau
jso.call("affiche", new String[] {String.valueOf(str)});
}
catch (Exception ex) {
ex.printStackTrace();
}
}
});
//On ajoute nos composants
this.getContentPane().add(bouton, BorderLayout.SOUTH);
this.getContentPane().add(label, BorderLayout.NORTH);
//Et le tour est joué !
}
}
|
Et
le résultat est
exactement celui escompté.
Dans ce code, il n'y a rien de
compliqué...
L'objet s'utilise facilement, il suffit de ne pas oublier de
gérer les exceptions et c'est bon.
Avant de terminer ce chapitre, vous
devez savoir que les applets n'ont pas le droit de tout faire sur une page web !

Applets et sécurité
En fait, au même titre que Javascript, les applets Java
n'ont pas le droit d'accéder à la machine du client. Pour faire simple, ils sont
confinés dans le navigateur web.
Et heureusement ! Vous imaginez un peu
toutes les dérives si ce genre de script ou de programme pouvait naturellement
avoir accès à votre PC ? Là, on pourrait devenir parano et il vaudrait
mieux...
- Est-ce que ce script n'est pas en train d'effacer un fichier de
configuration ?
- Il me semble que cette applet est en train d'accéder à un répertoire
sensible...
- ...
Bref, vous imaginez...
Cependant, il se peut que quelquefois, pour quelques rares cas, votre
applet doive accéder à des ressources de votre PC.
Exemple
Dans la boîte dans laquelle
je suis actuellement, nous sommes en train de développer une application, format
client léger (web), afin de gérer tous les processus industriels de la société,
dont la pesée de certains articles avec scan des documents en même
temps.
Nous avons donc fait une applet qui s'occupe de faire tout ça mais
pour communiquer avec les ports COM et le scanner, nous avons dû
signer notre applet.
Eh... Quoi ?

Nous
avons signé notre applet, c'est-à-dire que nous avons créé un certificat que
nous avons attribué à notre applet et que l'utilisateur DOIT soit accepter, soit
refuser au chargement de la page : ce certificat stipule que l'applet peut
accéder à des ressources de sa machine, et lui demande s'il veut lui faire
confiance .
Il n'est pas très pertinent de parler de la façon de signer
une applet : au pire, si vous avez vraiment besoin de ça,
Google est votre ami...

Vous
savez tout de même que les applets n'ont pas tous les droits sur une page web,
au même titre que Javascript.
Vous avez vu pas mal de choses, mine de
rien, dans ce chapitre.
On va faire un tour sur le topo habituel, suivi d'un
petit QCM...
Ce que vous devez retenir
- Les applets peuvent dériver de java.awt.Applet ou de javax.swing.JApplet.
- Les applets n'ont pas de constructeur mais une méthode init().
- En gros, les applets se comportent comme des applications fenêtrées.
- Vous pouvez communiquer de votre page web vers votre applet grâce à
l'instruction Javascript : document.applets.
- La communication inverse se fait via l'objet JSObject qui se
trouve dans l'archive plugin.jar dans le dossier
/lib de votre JRE (ou JDK).
- Par défaut, les applets n'ont pas accès aux
ressources de la machine client.
- Pour accéder à la machine du client, vous devrez
signer votre applet !
Maintenant, je pense que vous ne pourrez plus mélanger :
- Javascript et Java ;
- les applets et J2EE.
Sinon, vous êtes IMPARDONNABLES !

Après
cette petite dérive, je pense que nous pouvons continuer notre tour d'horizon de
l'API
swing !
En avant pour
les boîtes de dialogues !
Les boîtes de dialogue
Les boîtes de dialogue, c'est sûr, vous connaissez
!
Cependant, afin d'être sûr que nous parlons de la même chose, voici un
petit descriptif.
Une boîte de dialogue est une mini-fenêtre pouvant
servir à plusieurs choses :
- afficher une information (message d'erreur, warning...) ;
- demander une validation, une réfutation ou une annulation ;
- demander à l'utilisateur de saisir une information dont le système a
besoin ;
- ...
Vous pouvez voir que ces dernières peuvent servir à
beaucoup de choses.
Il faut toutefois les utiliser avec parcimonie : il est,
au final, assez pénible qu'une application ouvre une boîte de dialogue à chaque
notification, car toute boîte ouverte doit être fermée !
Pour ce point je
vous laisse seuls juges de leur utilisation... Et si nous commencions ?
Les boîtes d'information
L'objet que nous allons utiliser tout au long de ce
chapitre n'est autre que le
JOptionPane.
Objet assez complexe au
premier abord, mais fort pratique.
Voici à quoi ressemblent des boîtes de
dialogues "
informatives" :
Ces
boîtes n'ont pas vocation à participer à de quelconques traitements, elles
affichent juste un message pour l'utilisateur.
Voici le code utilisé pour
obtenir ces boîtes :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
//Déclaration des objets
JOptionPane jop1, jop2, jop3;
//Boîte du message d'information
//-------------------------------
jop1 = new JOptionPane();
jop1.showMessageDialog(null, "Message informatif", "Information", JOptionPane.INFORMATION_MESSAGE);
//Boîte du message préventif
//-------------------------------
jop2 = new JOptionPane();
jop2.showMessageDialog(null, "Message préventif", "Attention", JOptionPane.WARNING_MESSAGE);
//Boîte du message d'erreur
//-------------------------------
jop3 = new JOptionPane();
jop3.showMessageDialog(null, "Message d'erreur", "Erreur", JOptionPane.ERROR_MESSAGE);
|
Ces trois boîtes ne s'affichent
pas en même temps...
Pourquoi ça ?
Tout simplement parce qu'en
Java, mais aussi dans les autres langages, les boîtes de dialogue sont dites
modales.
Ceci signifie que lorsque qu'une
boîte fait son apparition, celle-ci bloque toute interaction possible avec un
autre composant que ceux présents sur la boîte. Ceci, tant que l'utilisateur n'a
pas mis fin au dialogue !
Même en créant un thread différent par boîte de
dialogue, vous ne pourrez pas toutes les voir !
Les 3 boîtes seront créées,
mais les boîtes s'affichant au centre de la fenêtre, vous ne pourrez en voir que
deux sur trois, en déplaçant la première.
Comme le montre le résultat de
ce code, vous ne pourrez pas déplacer la deuxième boîte à cause de la modalité
des boîtes :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 |
import javax.swing.JOptionPane;
public class Test {
public static void main(String[] args) {
Thread t = new Thread(new Runnable(){
public void run(){
JOptionPane jop1 = new JOptionPane();
jop1.showMessageDialog(null, "Message informatif", "Information", JOptionPane.INFORMATION_MESSAGE);
}
});
Thread t2 = new Thread(new Runnable(){
public void run(){
JOptionPane jop2 = new JOptionPane();
jop2.showMessageDialog(null, "Message préventif", "Attention", JOptionPane.WARNING_MESSAGE);
}
});
Thread t3 = new Thread(new Runnable(){
public void run(){
JOptionPane jop3 = new JOptionPane();
jop3.showMessageDialog(null, "Message d'erreur", "Erreur", JOptionPane.ERROR_MESSAGE);
}
});
t.start();
t2.start();
t3.start();
}
}
|
Ce qui nous donnerait (tout
dépend de l'ordonnanceur, vu qu'il y a trois thread...) :
Maintenant,
voyons de plus près la façon de construire un tel objet. Ici nous avons utilisé
la méthode :
showMessageDialog(Component parentComponent, String message, String title, int messageType); .
- ComponentparentComponent : correspond au
composant parent ; ici, il n'y en a aucun, nous mettons donc null.
- Stringmessage : permet de renseigner le
message à afficher dans la boîte de dialogue.
- Stringtitle : permet de donner un titre à
notre objet.
- intmessageType : permet de savoir s'il
s'agit d'un message d'information, de prévention ou d'erreur. Vous avez sans
doute remarqué que, mis à part le texte et le titre, seul ce champ change
entre les trois objets !
Il existe deux autres méthodes
showMessageDialog()
pour cet objet : une qui prend deux paramètres en moins (le titre et le
type de message), et une qui prend un paramètre en plus (l'icône à
utiliser).
Je pense qu'il est inutile de détailler la méthode avec les
paramètres en moins, mais voici des exemples de boîtes avec des icônes définies
par nos soins...
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 |
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
public class Test {
public static void main(String[] args) {
JOptionPane jop1, jop2, jop3;
jop1 = new JOptionPane();
ImageIcon img = new ImageIcon("images/information.png");
jop1.showMessageDialog(null, "Message informatif", "Information", JOptionPane.INFORMATION_MESSAGE, img);
jop2 = new JOptionPane();
img = new ImageIcon("images/warning.png");
jop2.showMessageDialog(null, "Message préventif", "Attention", JOptionPane.WARNING_MESSAGE, img);
jop3 = new JOptionPane();
img = new ImageIcon("images/erreur.png");
jop3.showMessageDialog(null, "Message d'erreur", "Erreur", JOptionPane.ERROR_MESSAGE, img);
}
}
|
Les images ont été trouvées sur Google puis rangées
dans un dossier "images" à la racine du projet Eclipse ! Je vous invite à
télécharger vos propres images et de faire vos propres tests...
Vous
constaterez aussi l'utilisation de l'objet
ImageIcon qui va lire le fichier image à
l'emplacement spécifié dans son constructeur.
Voici le résultat obtenu :
Ce
type de boîtes est très utile pour notifier à l'utilisateur qu'un traitement
s'est terminé ou qu'une erreur est survenue...
L'exemple le plus simple qui
me vient en tête serait une division par zéro :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
import javax.swing.JOptionPane;
public class Test {
public static void main(String[] args) {
int i = 100, j = 0;
try{
System.out.println("Résultat = " + (i/j));
}catch(ArithmeticException ex){
JOptionPane jop3 = new JOptionPane();
jop3.showMessageDialog(null, "Division par zéro détecté !", "Erreur fatale ! ! ", JOptionPane.ERROR_MESSAGE);
}
}
}
|
Les types de boîtes
Voici les types de boîtes que vous pouvez
utiliser (valable pour tout ce qui suit), triés par ordre alphabétique s'il vous
plaît...

:
- JOptionPane.ERROR_MESSAGE
- JOptionPane.INFORMATION_MESSAGE
- JOptionPane.PLAIN_MESSAGE
- JOptionPane.QUESTION_MESSAGE
- JOptionPane.WARNING_MESSAGE
Je
pense que vous devez mieux voir l'utilité de telles boîtes de
dialogues...
Nous allons donc poursuivre avec les boîtes de confirmation.
Les boîtes de confirmation
Comme leur nom l'indique, ces dernières permettent de
valider, de réfuter ou d'annuler une décision.
Nous utiliserons toujours
l'objet
JOptionPane mais, cette fois, avec la
méthode
showConfirmDialog() . Cette méthode retourne un entier qui correspond à
l'option que vous aurez choisie dans cette boîte :
Comme exemple, nous pouvons prendre notre
animation comme nous l'avons laissée la dernière fois.
Nous pourrions
utiliser une boîte de confirmation lorsque nous cliquerons sur l'un des boutons
contrôlant l'animation (
Go ou
Stop).
Pour ceux qui n'auraient pas conservé leur
projet, voici la classe
Panneau :
Secret (cliquez
pour afficher)
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 |
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class Panneau extends JPanel {
private int posX = -50;
private int posY = -50;
private int drawSize = 50;
//boolean pour le mode morphing et pour savoir si la taille doit réduire
private boolean morph = false, reduce = false;;
private String forme = "ROND";
//Le compteur de rafraîchissements
private int increment = 0;
public void paintComponent(Graphics g){
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
//Si le mode morphing est activé, on peint le morphing
if(this.morph)
drawMorph(g);
//sinon, mode normal
else
draw(g);
}
private void draw(Graphics g){
if(this.forme.equals("ROND")){
g.fillOval(posX, posY, 50, 50);
}
if(this.forme.equals("CARRE")){
g.fillRect(posX, posY, 50, 50);
}
if(this.forme.equals("TRIANGLE")){
int s1X = posX + 50/2;
int s1Y = posY;
int s2X = posX + 50;
int s2Y = posY + 50;
int s3X = posX;
int s3Y = posY + 50;
int[] ptsX = {s1X, s2X, s3X};
int[] ptsY = {s1Y, s2Y, s3Y};
g.fillPolygon(ptsX, ptsY, 3);
}
if(this.forme.equals("ETOILE")){
int s1X = posX + 50/2;
int s1Y = posY;
int s2X = posX + 50;
int s2Y = posY + 50;
g.drawLine(s1X, s1Y, s2X, s2Y);
int s3X = posX;
int s3Y = posY + 50/3;
g.drawLine(s2X, s2Y, s3X, s3Y);
int s4X = posX + 50;
int s4Y = posY + 50/3;
g.drawLine(s3X, s3Y, s4X, s4Y);
int s5X = posX;
int s5Y = posY + 50;
g.drawLine(s4X, s4Y, s5X, s5Y);
g.drawLine(s5X, s5Y, s1X, s1Y);
}
}
/**
* Méthode qui peint le morphing
* @param g
*/
private void drawMorph(Graphics g){
//On incrémente le tour
increment++;
//On regarde si on doit réduire ou non
if(drawSize >= 50)reduce = true;
if(drawSize <= 10)reduce = false;
if(reduce)
drawSize = drawSize - getUsedSize();
else
drawSize = drawSize + getUsedSize();
if(this.forme.equals("ROND")){
g.fillOval(posX, posY, drawSize, drawSize);
}
if(this.forme.equals("CARRE")){
g.fillRect(posX, posY, drawSize, drawSize);
}
if(this.forme.equals("TRIANGLE")){
int s1X = posX + drawSize/2;
int s1Y = posY;
int s2X = posX + drawSize;
int s2Y = posY + drawSize;
int s3X = posX;
int s3Y = posY + drawSize;
int[] ptsX = {s1X, s2X, s3X};
int[] ptsY = {s1Y, s2Y, s3Y};
g.fillPolygon(ptsX, ptsY, 3);
}
if(this.forme.equals("ETOILE")){
int s1X = posX + drawSize/2;
int s1Y = posY;
int s2X = posX + drawSize;
int s2Y = posY + drawSize;
g.drawLine(s1X, s1Y, s2X, s2Y);
int s3X = posX;
int s3Y = posY + drawSize/3;
g.drawLine(s2X, s2Y, s3X, s3Y);
int s4X = posX + drawSize;
int s4Y = posY + drawSize/3;
g.drawLine(s3X, s3Y, s4X, s4Y);
int s5X = posX;
int s5Y = posY + drawSize;
g.drawLine(s4X, s4Y, s5X, s5Y);
g.drawLine(s5X, s5Y, s1X, s1Y);
}
}
/**
* Méthode qui retourne le nombre à retrancher (ou ajouter) pour le morphing
* @return res
*/
private int getUsedSize(){
int res = 0;
//Si le nombre de tours est de 10
//On réinitialise l'incrément et on retourne 1
if(increment / 10 == 1){
increment = 0;
res = 1;
}
return res;
}
public int getDrawSize(){
return drawSize;
}
public boolean isMorph(){
return morph;
}
public void setMorph(boolean bool){
this.morph = bool;
//On réinitialise la taille
drawSize = 50;
}
public void setForme(String form){
this.forme = form;
}
public int getPosX() {
return posX;
}
public void setPosX(int posX) {
this.posX = posX;
}
public int getPosY() {
return posY;
}
public void setPosY(int posY) {
this.posY = posY;
}
}
|
Voici le code de
notre classe
Fenetre :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JButton bouton = new JButton("Go");
private JButton bouton2 = new JButton("Stop");
private JPanel container = new JPanel();
private JLabel label = new JLabel("Choix de la forme");
private int compteur = 0;
private boolean animated = true;
private boolean backX, backY;
private int x,y ;
private Thread t;
private JComboBox combo = new JComboBox();
private JCheckBox morph = new JCheckBox("Morphing");
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
bouton.addActionListener(new BoutonListener());
bouton2.addActionListener(new Bouton2Listener());
bouton2.setEnabled(false);
JPanel south = new JPanel();
south.add(bouton);
south.add(bouton2);
container.add(south, BorderLayout.SOUTH);
combo.addItem("ROND");
combo.addItem("CARRE");
combo.addItem("TRIANGLE");
combo.addItem("ETOILE");
combo.addActionListener(new FormeListener());
morph.addActionListener(new MorphListener());
JPanel top = new JPanel();
top.add(label);
top.add(combo);
top.add(morph);
container.add(top, BorderLayout.NORTH);
this.setContentPane(container);
this.setVisible(true);
}
private void go(){
x = pan.getPosX();
y = pan.getPosY();
while(this.animated){
//System.out.println("OK");
//Si le mode morphing est activé, on utilise la taille actuelle de la forme
if(pan.isMorph())
{
if(x < 1)backX = false;
if(x > pan.getWidth() - pan.getDrawSize())backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight() - pan.getDrawSize())backY = true;
}
//Sinon, comme d'habitude
else
{
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
}
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous lancer l'animation ?", "Lancement de l'animation", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if(option == JOptionPane.OK_OPTION)
{
animated = true;
t = new Thread(new PlayAnimation());
t.start();
bouton.setEnabled(false);
bouton2.setEnabled(true);
}
}
}
class Bouton2Listener implements ActionListener{
public void actionPerformed(ActionEvent e) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous arrêter l'animation ?", "Arrêt de l'animation", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if(option != JOptionPane.NO_OPTION && option != JOptionPane.CANCEL_OPTION && option != JOptionPane.CLOSED_OPTION)
{
animated = false;
bouton.setEnabled(true);
bouton2.setEnabled(false);
}
}
}
class PlayAnimation implements Runnable{
public void run() {
go();
}
}
class FormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
pan.setForme(combo.getSelectedItem().toString());
}
}
class MorphListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Si la case est cochée, activation du mode morphing
if(morph.isSelected())pan.setMorph(true);
//Sinon rien !
else pan.setMorph(false);
}
}
}
|
L'une des instructions
intéressantes se trouve ici :
Code : Java
1
2
3
4
5
6
7
8
9
10
11 |
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous lancer l'animation ?", "Lancement de l'animation", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if(option == JOptionPane.OK_OPTION)
{
animated = true;
t = new Thread(new PlayAnimation());
t.start();
bouton.setEnabled(false);
bouton2.setEnabled(true);
}
|
Et l'autre, là :
Code : Java
1
2
3
4
5
6
7
8
9 |
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous arrêter l'animation ?", "Arrêt de l'animation", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if(option != JOptionPane.NO_OPTION && option != JOptionPane.CANCEL_OPTION && option != JOptionPane.CLOSED_OPTION)
{
animated = false;
bouton.setEnabled(true);
bouton2.setEnabled(false);
}
|
Voyons ce qu'il se passe ici
:
- nous initialisons notre objet JOptionPane : rien d'étonnant ;
- par contre, au lieu d'afficher la boîte, nous affectons le résultat que
renvoie la méthode showConfirmDialog() dans une
variable de type int ;
- nous nous servons de cette variable afin de savoir quel bouton a été
cliqué (oui ou non).
En fait, lorsque vous cliquez sur l'un
des deux boutons présents sur cette boîte, vous pouvez affecter ici deux valeurs
de type
int
:
- la valeur correspondant à l'entier JOptionPane.OK_OPTION vaut 0 (JOptionPane.YES_OPTION à la même valeur) ;
- la valeur correspondant à l'entier JOptionPane.NO_OPTION vaut 1 ;
- la valeur correspondant à l'entier JOptionPane.CANCEL_OPTION pour la boîte apparaissant lors du clic
sur "Stop" vaut 2 ;
- la valeur correspondant à l'entier JOptionPane.CLOSED_OPTION pour la même boîte que ci-dessus, vaut
-1.
En comparant la valeur de notre entier avec l'une des deux
options, nous pouvons en déduire quel bouton a été cliqué et donc agir en
conséquence !

Voici
deux screenshots du résultat obtenu :
Vous
commencez à maîtriser les
JOptionPane, on dirait...
Si on
continuait ?...
Les boîtes de saisie
Je suis sûr que vous avez deviné ce à quoi ces boîtes
peuvent servir...

Oui,
tout à fait, nous allons pouvoir faire des saisies dans ces boîtes ! Et même
encore mieux... Nous pourrons même avoir une boîte de dialogue qui propose des
choix dans une liste déroulante.

Je
sens que vous êtes pressés de commencer. Alors allons-y.
Vous savez déjà que
nous allons utiliser l'objet
JOptionPane, et les plus curieux d'entre
vous ont sûrement dû jeter un oeil aux autres méthodes proposés par cet objet...
Ici, nous allons utiliser la méthode
showInputDialog(Component parent, String message, String title, int messageType)
.
Voici un code mettant en oeuvre cette méthode :
Code : Java
1
2
3
4
5
6
7
8
9
10 |
import javax.swing.JOptionPane;
public class Test {
public static void main(String[] args) {
JOptionPane jop = new JOptionPane(), jop2 = new JOptionPane();
String nom = jop.showInputDialog(null, "Veuillez décliner votre identité !", "Gendarmerie nationale !", JOptionPane.QUESTION_MESSAGE);
jop2.showMessageDialog(null, "Votre nom est " + nom, "Identité", JOptionPane.INFORMATION_MESSAGE);
}
}
|
Vous pouvez constater que cette méthode retourne
une chaîne de caractères !
Voici le résultat :
Rien
d'extraordinaire...
Maintenant, voyons comment on incorpore une liste
dans une boîte de ce genre... Vous allez voir, c'est simplissime
!
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
import javax.swing.JOptionPane;
public class Test {
public static void main(String[] args) {
String[] sexe = {"masculin", "féminin", "indéterminé"};
JOptionPane jop = new JOptionPane(), jop2 = new JOptionPane();
String nom = (String)jop.showInputDialog(null,
"Veuillez décliner votre sexe !",
"Gendarmerie nationale !",
JOptionPane.QUESTION_MESSAGE,
null,
sexe,
sexe[2]);
jop2.showMessageDialog(null, "Votre sexe est " + nom, "Etat civil", JOptionPane.INFORMATION_MESSAGE);
}
}
|
Ce code nous donne :
Nous
avons utilisé la méthode avec des paramètres en plus, les voici :
- les quatre premiers, vous connaissez maintenant ;
- le deuxième null
correspond à l'icône que vous souhaitez passer ;
- ensuite, vous devez passer un tableau de String afin de remplir la combo dans la
boîte ;
- le dernier paramètre correspond à la valeur à sélectionner par défaut.
Cette méthode retourne aussi un objet, mais de type
Object,
comme si vous récupériez la valeur directement dans la combo !
Donc pensez à faire un cast.
Voici
maintenant une variante de ce que vous venez de voir : nous allons utiliser ici
la méthode
showOptionDialog() .
Celle-ci fonctionne à peu près comme la méthode
précédente sauf qu'elle prend un paramètre de plus et que le type de retour
n'est pas un objet, mais un entier !
Ce type de boîte propose, au lieu
d'une combo, un choix de boutons correspondant aux éléments passés en paramètres
(tableau de String) ; elle prend aussi une valeur par défaut mais retourne
l'indice de l'élément dans la liste, et non l'élément lui-même.
Je pense
que vous vous y connaissez assez pour comprendre le code suivant :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
import javax.swing.JOptionPane;
public class Test {
public static void main(String[] args) {
String[] sexe = {"masculin", "féminin", "indéterminé"};
JOptionPane jop = new JOptionPane(), jop2 = new JOptionPane();
int rang = jop.showOptionDialog(null,
"Veuillez décliner votre sexe !",
"Gendarmerie nationale !",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
sexe,
sexe[2]);
jop2.showMessageDialog(null, "Votre sexe est " + sexe[rang], "Etat civil", JOptionPane.INFORMATION_MESSAGE);
}
}
|
Ce qui nous donne :
Voilà,
vous en avez terminé avec les boîtes de saisie...
Cependant, vous devez vous
demander si vous ne pouvez pas ajouter des composants sur ces boîtes.
C'est
vrai, vous pourriez avoir besoin de plus de renseignements, on ne sait jamais...
Je vous propose donc de vous montrer comment créer vos propres boîtes de
dialogue !
En avant, mes ZérOs ! Hissez haut !
Des boîtes de dialogue personnalisées
Je me doute bien que vous devez être impatients de
faire vos propres boîtes de dialogue...
Il est vrai que dans certains cas,
vous en aurez besoin, donc allons-y gaiement !

Je
vais maintenant vous révéler un secret bien gardé : les boîtes de dialogue
héritent de la classe
JDialog.
Vous avez donc deviné que nous
allons créer une classe dérivée de cette dernière.
Commençons par faire
un nouveau projet.
Créez une nouvelle classe dans Eclipse, appelons-la
ZDialog
héritée de la classe citée ci-dessus, et mettez-y le code
suivant :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
import javax.swing.JDialog;
import javax.swing.JFrame;
public class ZDialog extends JDialog {
public ZDialog(JFrame parent, String title, boolean modal){
//On appelle le construteur de JDialog correspondant
super(parent, title, modal);
//On spécifie une taille
this.setSize(200, 80);
//La position
this.setLocationRelativeTo(null);
//La boîte ne devra pas être redimensionnable
this.setResizable(false);
//Enfin on l'affiche
this.setVisible(true);
//Tout ceci ressemble à ce que nous faisons depuis le début avec notre JFrame...
}
}
|
Maintenant, faisons une
classe qui va tester notre
ZDialog :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 |
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Fenetre extends JFrame {
private JButton bouton = new JButton("Appel à la ZDialog");
public Fenetre(){
this.setTitle("Ma JFrame");
this.setSize(300, 100);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.getContentPane().setLayout(new FlowLayout());
this.getContentPane().add(bouton);
bouton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
ZDialog zd = new ZDialog(null, "Coucou les ZérOs", true);
}
});
this.setVisible(true);
}
public static void main(String[] main){
Fenetre fen = new Fenetre();
}
}
|
Voilà le résultat ; bon,
c'est un début :
Je
pense que vous avez deviné à quoi servaient les paramètres du constructeur...
Mais nous allons tout de même les expliciter :
- JFrame Parent correspond à l'objet parent ;
- String title correspond au titre de notre boîte ;
- boolean modal correspond à la modalité. true : boîte modale,
false :
boîte non modale.
Rien de compliqué... Il est donc temps de
mettre des composants sur notre objet...
Par contre, vous conviendrez que
lorsque nous faisons un tel composant, nous voulons quelque chose de plus qu'une
réponse à une question ouverte (oui / non), une chaîne de caractères ou encore
un choix dans une liste... Nous en voulons encore plus ! Plusieurs saisies, avec
plusieurs listes :
en même temps !
Mais vous avez vu que nous devrons récupérer les informations choisies
dans certains cas, mais pas tous : nous allons donc devoir déterminer les
différents cas ainsi que les choses à faire.
Nous partons du fait que
notre boîte aura un bouton "
OK" et
"
Annuler" : dans le cas où
l'utilisateur clique sur "
OK", on
récupérera les informations, et si l'utilisateur clique sur "
Annuler", non.
Tout ceci en tenant compte de
la modalité de notre boîte...
D'ailleurs, comment va-t-on faire pour dire à notre
boîte de mettre fin au dialogue ?
Tout simplement en utilisant la
méthode
setVisible(false); ,
cette instruction met fin au dialogue !
Ceci signifie aussi que le dialogue
commence au moment où l'instruction
setVisible(true); est exécutée. De ce fait, nous allons sortir cette
instruction du constructeur de notre objet et nous allons la mettre dans une
méthode à part.

Maintenant,
nous devrons pouvoir dire à notre boîte de divulguer ses informations ou non.
C'est pour cela que nous allons utiliser un boolean - appelons-le
sendData -, initialisé à
false mais qui passera
à
true si
on clique sur "
OK".
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12 |
//Cas où notre ZDialog renverra le contenu
//D'un JTextField nommé jtf
public String showZDialog(){
this.sendData = false;
//DEBUT DU DIALOGUE
this.setVisible(true);
//Le dialogue prend fin
//SI on a cliqué sur OK, on envoie, sinon on envoie chaîne vide !
return (this.sendData)? jtf.getText() : "";
}
|
Il nous reste un dernier
point à gérer...
Comment récupérer les informations saisies
dans notre boîte depuis notre fenêtre, vu que nous voulons plusieurs
informations ?
C'est vrai qu'on ne peut retourner qu'une
valeur à la fois...

Mais
il peut y avoir plusieurs réponses à cette question.
- Dans le cas où nous n'avons qu'un composant, nous pouvons adapter la
méthode showZDialog() au type de
retour du composant utilisé.
- Dans notre cas, nous voulons plusieurs composants, donc plusieurs valeurs,
vous pouvez :
- retourner une collection de valeurs (ArrayList...) ;
- faire des accesseurs dans votre ZDialog ;
- créer un objet dont le rôle est de collecter les informations de votre
boîte et de retourner cet objet...
- ...
Nous allons opter pour un objet qui
collectera les informations et nous retournerons cet objet à la fin de la
méthode
showZDialog().
Avant de
nous lancer dans la création de cet objet, nous devons savoir ce que nous allons
mettre dans notre boîte...
Nous allons faire une boîte permettant de
spécifier les caractéristiques d'un personnage de jeu vidéo :
- son nom : un champ de saisie ;
- son sexe : une combo ;
- sa taille : un champ de saisie ;
- sa couleur de cheveux : une combo ;
- sa tranche d'âge : des radios.
Pour le placement des composants, l'objet
JDialog est exactement identique à
un objet JFrame (BorderLayout par défaut, ajout de composant au
conteneur...).
Nous pouvons donc faire notre objet contenant les
informations de notre boîte de dialogue, je l'ai appelé
ZDialogInfo :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 |
public class ZDialogInfo {
private String nom, sexe, age, cheveux, taille;
public ZDialogInfo(){}
public ZDialogInfo(String nom, String sexe, String age,
String cheveux, String taille){
this.nom = nom;
this.sexe = sexe;
this.age = age;
this.cheveux = cheveux;
this.taille = taille;
}
//------------------------------------
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
//------------------------------------
public String getSexe() {
return sexe;
}
public void setSexe(String sexe) {
this.sexe = sexe;
}
//------------------------------------
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
//------------------------------------
public String getCheveux() {
return cheveux;
}
public void setCheveux(String cheveux) {
this.cheveux = cheveux;
}
//------------------------------------
public String getTaille() {
return taille;
}
public void setTaille(String taille) {
this.taille = taille;
}
//-------------------------------------
public String toString(){
String str;
if(this.nom != null && this.sexe != null &&
this.taille != null && this.age != null &&
this.cheveux != null){
str = "Description de l'objet InfoZDialog";
str += "Nom : " + this.nom + "\n";
str += "Sexe : " + this.sexe + "\n";
str += "Age : " + this.age + "\n";
str += "Cheveux : " + this.cheveux + "\n";
str += "Taille : " + this.taille + "\n";
}
else{
str = "Aucune information !";
}
return str;
}
}
|
L'avantage avec cette
méthode, c'est que nous n'avons pas à nous soucier de savoir si nous avons
annulé la saisie ou non : l'objet d'information renverra toujours quelque
chose...
Voici le code source de notre boîte perso :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.ButtonGroup;
import javax.swing.JTextField;
public class ZDialog extends JDialog {
private ZDialogInfo zInfo = new ZDialogInfo();
private boolean sendData;
private JLabel nomLabel, sexeLabel, cheveuxLabel, ageLabel, tailleLabel, taille2Label, icon;
private JRadioButton tranche1, tranche2, tranche3, tranche4;
private JComboBox sexe, cheveux;
private JTextField nom, taille;
/**
* Constructeur
* @param parent
* @param title
* @param modal
*/
public ZDialog(JFrame parent, String title, boolean modal){
super(parent, title, modal);
this.setSize(550, 270);
this.setLocationRelativeTo(null);
this.setResizable(false);
this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
this.initComponent();
}
/**
* Méthode appelée pour utiliser la boîte
* @return zInfo
*/
public ZDialogInfo showZDialog(){
this.sendData = false;
this.setVisible(true);
return this.zInfo;
}
/**
* Initialise le contenu de la boîte
*/
private void initComponent(){
//Icone
icon = new JLabel(new ImageIcon("images/icone.jpg"));
JPanel panIcon = new JPanel();
panIcon.setBackground(Color.white);
panIcon.setLayout(new BorderLayout());
panIcon.add(icon);
//Le nom
JPanel panNom = new JPanel();
panNom.setBackground(Color.white);
panNom.setPreferredSize(new Dimension(220, 60));
nom = new JTextField();
nom.setPreferredSize(new Dimension(100, 25));
panNom.setBorder(BorderFactory.createTitledBorder("Nom du personnage"));
nomLabel = new JLabel("Saisir un nom :");
panNom.add(nomLabel);
panNom.add(nom);
//Le sexe
JPanel panSexe = new JPanel();
panSexe.setBackground(Color.white);
panSexe.setPreferredSize(new Dimension(220, 60));
panSexe.setBorder(BorderFactory.createTitledBorder("Sexe du personnage"));
sexe = new JComboBox();
sexe.addItem("Masculin");
sexe.addItem("Féminin");
sexe.addItem("Indéterminé");
sexeLabel = new JLabel("Sexe : ");
panSexe.add(sexeLabel);
panSexe.add(sexe);
//L'âge
JPanel panAge = new JPanel();
panAge.setBackground(Color.white);
panAge.setBorder(BorderFactory.createTitledBorder("Age du personnage"));
panAge.setPreferredSize(new Dimension(440, 60));
tranche1 = new JRadioButton("15 - 25 ans");
tranche1.setSelected(true);
tranche2 = new JRadioButton("26 - 35 ans");
tranche3 = new JRadioButton("36 - 50 ans");
tranche4 = new JRadioButton("+ de 50 ans");
ButtonGroup bg = new ButtonGroup();
bg.add(tranche1);
bg.add(tranche2);
bg.add(tranche3);
bg.add(tranche4);
panAge.add(tranche1);
panAge.add(tranche2);
panAge.add(tranche3);
panAge.add(tranche4);
//La taille
JPanel panTaille = new JPanel();
panTaille.setBackground(Color.white);
panTaille.setPreferredSize(new Dimension(220, 60));
panTaille.setBorder(BorderFactory.createTitledBorder("Taille du personnage"));
tailleLabel = new JLabel("Taille : ");
taille2Label = new JLabel(" cm");
taille = new JTextField("180");
taille.setPreferredSize(new Dimension(90, 25));
panTaille.add(tailleLabel);
panTaille.add(taille);
panTaille.add(taille2Label);
//La couleur des cheveux
JPanel panCheveux = new JPanel();
panCheveux.setBackground(Color.white);
panCheveux.setPreferredSize(new Dimension(220, 60));
panCheveux.setBorder(BorderFactory.createTitledBorder("Couleur de cheveux du personnage"));
cheveux = new JComboBox();
cheveux.addItem("Blond");
cheveux.addItem("Brun");
cheveux.addItem("Roux");
cheveux.addItem("Blanc");
cheveuxLabel = new JLabel("Cheveux");
panCheveux.add(cheveuxLabel);
panCheveux.add(cheveux);
JPanel content = new JPanel();
content.setBackground(Color.white);
content.add(panNom);
content.add(panSexe);
content.add(panAge);
content.add(panTaille);
content.add(panCheveux);
JPanel control = new JPanel();
JButton okBouton = new JButton("OK");
okBouton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
zInfo = new ZDialogInfo(nom.getText(), (String)sexe.getSelectedItem(), getAge(), (String)cheveux.getSelectedItem() ,getTaille());
setVisible(false);
}
public String getAge(){
return (tranche1.isSelected()) ? tranche1.getText() : (tranche2.isSelected()) ? tranche2.getText() : (tranche3.isSelected()) ? tranche3.getText() : (tranche4.isSelected()) ? tranche4.getText() : tranche1.getText();
}
public String getTaille(){
return (taille.getText().equals("")) ? "180" : taille.getText();
}
});
JButton cancelBouton = new JButton("Annuler");
cancelBouton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
setVisible(false);
}
});
control.add(okBouton);
control.add(cancelBouton);
this.getContentPane().add(panIcon, BorderLayout.WEST);
this.getContentPane().add(content, BorderLayout.CENTER);
this.getContentPane().add(control, BorderLayout.SOUTH);
}
}
|
J'ai ajouté une image, mais vous n'y êtes
nullement obligés !
Vous constaterez aussi que j'ai ajouté une bordure à nos
JPanel afin de faire plus...
Zoli...
Et le code source permettant de tester cette boîte
:
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 |
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class Fenetre extends JFrame {
private JButton bouton = new JButton("Appel à la ZDialog");
public Fenetre(){
this.setTitle("Ma JFrame");
this.setSize(300, 100);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.getContentPane().setLayout(new FlowLayout());
this.getContentPane().add(bouton);
bouton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
ZDialog zd = new ZDialog(null, "Coucou les ZérOs", true);
ZDialogInfo zInfo = zd.showZDialog();
JOptionPane jop = new JOptionPane();
jop.showMessageDialog(null, zInfo.toString(), "Informations personnage", JOptionPane.INFORMATION_MESSAGE);
}
});
this.setVisible(true);
}
public static void main(String[] main){
Fenetre fen = new Fenetre();
}
}
|
Voici des screenshots obtenus
De la ZDialog
Lorsqu'on valide la saisie
Lorsqu'on annule la saisie
Voilà
: nous arrivons à la fin de chapitre, riche en nouveautés...

En
route pour le topo habituel et le petit QCM des familles...
Ce qu'il faut retenir
- Les boîtes de dialogue s'utilisent, sauf boîtes personnalisées, avec
l'objet JOptionPane.
- La méthode showMessageDialog() permet d'afficher un
message informatif.
- La méthode showConfirmDialog() permet d'afficher une boîte
attendant une réponse à une question ouverte (oui / non).
- La méthode citée ci-dessus retourne un entier correspondant au bouton sur
lequel vous avez cliqué.
- La méthode showInputDialog()
affiche une boîte attendant une saisie de la part de l'utilisateur ou une
sélection dans une liste.
- Cette méthode retourne soit un String dans le cas d'une saisie, soit un
Object
dans le cas d'une liste.
- La méthode showOptionDialog()
affiche une boîte attendant le clic sur une option proposée à l'utilisateur.
- Celle-ci retourne l'indice de l'élément sur lequel vous avez cliqué,
ou un indice négatif pour tous les autres cas
!
- Les boîtes de dialogue sont dites modales : aucune interaction hors de la
boîte n'est possible tant que celle-ci n'est pas fermée !
- Pour faire une boîte de dialogue personnalisée, vous devez créer une
classe héritée de JDialog.
- Pour les boîtes personnalisées, le dialogue commence lorsque la méthode
setVisible(true) est
invoquée et se termine lorsque la méthode setVisible(false) est appelée !
Chapitre très simple, là aussi, mais je tenais à vous
féliciter.

Vous
avez dû remarquer que vous avez réellement progressé en Java depuis le début de
ce tuto, et vous devez vous rendre compte qu'en définitive, faire des IHM en
Java n'est pas si compliqué qu'il n'y paraît... Il y a beaucoup de choses à
savoir, mais le plus important n'est pas de tout connaître par coeur, mais de
savoir retrouver l'information rapidement !
Trêve de compliments, sinon
vous ne travaillerez plus...

Je
vous propose donc maintenant de continuer par un chapitre qui risque de vous
plaire tout autant :
Les menus.
Les menus
Tout le monde sait ce qu'est un menu.
Une barre dans
laquelle se trouve une ou plusieurs listes déroulantes, elles-mêmes composées de
listes.
Vous avez un exemple sous les yeux, tout en haut de votre navigateur
internet.
Dans ce chapitre nous allons voir comment ajouter ce genre de
composants dans vos fenêtres, mais aussi comment les utiliser.
Nous
aborderons aussi la façon d'utiliser ce qu'on appelle des
accélérateurs
: par exemple, sous Firefox, si vous faites CTRL + T, vous ouvrez un
nouvel onglet. Ce raccourci est un exemple d'accélérateur.
Au cours de ce
chapitre nous verrons aussi les
mnémoniques : dans la barre de
menu de votre navigateur, vous pouvez voir qu'une des lettres de chaque élément
de menu est soulignée, ceci signifie que si vous appuyez simultanément sur ALT +
<la lettre soulignée>, vous déroulerez le menu correspondant. Voici ce
qu'on nomme un mnémonique.
Ce type de menu, dit barre de menus, est le
plus courant ; mais nous verrons aussi comment utiliser un menu
contextuel.
Vous savez, celui qui apparaît lorsque vous faites un clic
droit...

Et
en bonus, nous apprendrons aussi à utiliser les barres d'outils.
Allez,
assez de blabla ! Let's go.
La barre de menus et les éléments de menu
Vous vous rappelez que j'ai mentionné une
MenuBar faisant partie de la
composition de l'objet
JFrame.
Le moment est venu pour
vous d'utiliser un tel composant : cependant, celui cité ci-dessus appartient au
package
java.awt . Dans ce chapitre nous
utiliserons son homologue, l'objet
JMenuBar, présent dans le package
javax.swing .
Un menu simple
Afin de travailler avec des menus, nous allons
avoir besoin :
- d'objets JMenu
: titre global d'un point de menu. Regardez en haut de
votre navigateur ;
- d'objets JMenuItem
: éléments composants nos menus.
Ici, afin
de pouvoir utiliser nos futurs menus, nous allons devoir coder des
implémentations de l'interface
ActionListener : vous la connaissez bien,
celle-là !

Ces
implémentations serviront à écouter les objets
JMenuItem. Ce sont ces objets qui
déclencheront tel ou tel traitement. Les
JMenu, eux, ont un comportement
automatique. Si on clique sur un titre de menu, celui-ci se déroule tout seul
et, dans le cas où nous avons un tel objet présent dans un autre
JMenu, une autre liste
se déroulera toute seule !

En
bref, nous avons à gérer qu'une partie de tous ces objets.
Bon : nous allons
pouvoir commencer. Je vous propose donc d'enlever tous les composants (bouton,
combo, checkbox) de notre animation et de gérer tout cela par le biais d'un
menu.
Avant de nous lancer dans cette tâche, je vais vous montrer un
exemple d'utilisation, histoire de vous familiariser. Voici un code d'exemple :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 |
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
public class ZFenetre extends JFrame {
private JMenuBar menuBar = new JMenuBar();
private JMenu test1 = new JMenu("Fichier");
private JMenu test2 = new JMenu("Edition");
private JMenuItem item1 = new JMenuItem("Ouvrir");
private JMenuItem item2 = new JMenuItem("Fermer");
private JMenuItem item3 = new JMenuItem("Lancer");
private JMenuItem item4 = new JMenuItem("Arrêter");
public ZFenetre(){
this.setSize(400, 200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
//On initialise nos menus
//--------------------------
this.test1.add(item1);
item2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
});
this.test1.add(item2);
this.test2.add(item3);
this.test2.add(item4);
//L'ordre d'ajout va déterminer l'ordre d'apparition dans le menu de gauche à droite
//Le premier ajouté sera tout à gauche de la barre de menu et inversement pour le dernier
this.menuBar.add(test1);
this.menuBar.add(test2);
//-------------------------
this.setJMenuBar(menuBar);
this.setVisible(true);
}
}
|
L'action attachée au JMenutItem
"Fermer" permet de quitter
l'application.
Et le résultat de ce code :
Voici
notre exemple un peu plus élaboré :
Code :
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 |
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;
public class ZFenetre extends JFrame {
private JMenuBar menuBar = new JMenuBar();
private JMenu test1 = new JMenu("Fichier");
private JMenu test1_2 = new JMenu("Sous ficher");
private JMenu test2 = new JMenu("Edition");
private JMenuItem item1 = new JMenuItem("Ouvrir");
private JMenuItem item2 = new JMenuItem("Fermer");
private JMenuItem item3 = new JMenuItem("Lancer");
private JMenuItem item4 = new JMenuItem("Arrêter");
private JCheckBoxMenuItem jcmi1 = new JCheckBoxMenuItem("Choix 1");
private JCheckBoxMenuItem jcmi2 = new JCheckBoxMenuItem("Choix 2");
private JRadioButtonMenuItem jrmi1 = new JRadioButtonMenuItem("Radio 1");
private JRadioButtonMenuItem jrmi2 = new JRadioButtonMenuItem("Radio 2");
public static void main(String[] args){
ZFenetre zFen = new ZFenetre();
}
public ZFenetre(){
this.setSize(400, 200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
//On initialise nos menus
//--------------------------
this.test1.add(item1);
//On ajoute les éléments dans notre sous-menu
this.test1_2.add(jcmi1);
this.test1_2.add(jcmi2);
//Ajout d'un séparateur
this.test1_2.addSeparator();
//On met nos radios dans un ButtonGroup
ButtonGroup bg = new ButtonGroup();
bg.add(jrmi1);
bg.add(jrmi1);
//On présélectionne la première radio
jrmi1.setSelected(true);
this.test1_2.add(jrmi1);
this.test1_2.add(jrmi2);
//Ajout du sous-menu dans notre menu
this.test1.add(this.test1_2);
//Ajout d'un séparateur
this.test1.addSeparator();
item2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
});
this.test1.add(item2);
this.test2.add(item3);
this.test2.add(item4);
//L'ordre d'ajout va déterminer l'ordre d'apparition dans le menu de gauche à droite
//Le premier ajouté sera tout à gauche de la barre de menu et inversement pour le dernier
this.menuBar.add(test1);
this.menuBar.add(test2);
//-------------------------
this.setJMenuBar(menuBar);
this.setVisible(true);
}
}
|
Et voilà le rendu de ce code
:
Vous
pouvez voir qu'il n'y a rien de difficile à créer un menu. Je vous propose donc
d'en créer un pour notre animation. Nous allons faire ceci petit à petit. Nous
gèrerons les événements ensuite. Pour le moment, nous allons avoir besoin :
- d'un menu animation, pour
lancer, arrêter (par défaut à setEnabled(false) ) ou quitter l'animation ;
- d'un menu forme, afin de savoir
quel type de forme utiliser (sous-menu + une radio par forme) et si celle-ci
est en mode morphing (case à cocher) ;
- d'un menu à propos, avec un zoli
"?" qui va ouvrir une boîte de dialogue.
N'effacez surtout pas
les implémentations pour les événements, retirez seulement les composants
utilisant les implémentations et créez votre menu !
Vous serez sans doute obligés de mettre quelques
instructions en commentaire... Mais nous y reviendrons.
Je vous laisse
faire, vous devriez y arriver sans problème... Prenez votre temps, réfléchissez,
et allez-y !

Voici
le code que vous devriez avoir (ou un code s'en approchant) :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JPanel container = new JPanel();
private int compteur = 0;
private boolean animated = true;
private boolean backX, backY;
private int x,y ;
private Thread t;
private JMenuBar menuBar = new JMenuBar();
private JMenu animation = new JMenu("Animation"),
forme = new JMenu("Forme"),
typeForme = new JMenu("Type de forme"),
aPropos = new JMenu("À propos");
private JMenuItem lancer = new JMenuItem("Lancer l'animation"),
arreter = new JMenuItem("Arrêter l'animation"),
quitter = new JMenuItem("Quitter"),
aProposItem = new JMenuItem("?");
private JCheckBoxMenuItem morph = new JCheckBoxMenuItem("Morphing");
private JRadioButtonMenuItem carre = new JRadioButtonMenuItem("Carré"),
rond = new JRadioButtonMenuItem("Rond"),
triangle = new JRadioButtonMenuItem("Triangle"),
etoile = new JRadioButtonMenuItem("Etoile");
private ButtonGroup bg = new ButtonGroup();
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
this.setContentPane(container);
this.initMenu();
this.setVisible(true);
}
private void initMenu(){
//Menu animation
animation.add(lancer);
arreter.setEnabled(false);
animation.add(arreter);
animation.addSeparator();
//Pour quitter l'application
quitter.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
System.exit(0);
}
});
animation.add(quitter);
//Menu forme
bg.add(carre);
bg.add(triangle);
bg.add(rond);
bg.add(etoile);
typeForme.add(rond);
typeForme.add(carre);
typeForme.add(triangle);
typeForme.add(etoile);
rond.setSelected(true);
forme.add(typeForme);
forme.add(morph);
//menu à propos
aPropos.add(aProposItem);
//Ajout des menus dans la barre de menus
menuBar.add(animation);
menuBar.add(forme);
menuBar.add(aPropos);
//Ajout de la barre de menus sur la fenêtre
this.setJMenuBar(menuBar);
}
private void go(){
x = pan.getPosX();
y = pan.getPosY();
while(this.animated){
//System.out.println("OK");
//Si le mode morphing est activé, on utilise la taille actuelle de la forme
if(pan.isMorph())
{
if(x < 1)backX = false;
if(x > pan.getWidth() - pan.getDrawSize())backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight() - pan.getDrawSize())backY = true;
}
//Sinon, comme d'habitude
else
{
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
}
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous lancer l'animation ?", "Lancement de l'animation", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE);
if(option == JOptionPane.OK_OPTION)
{
lancer.setEnabled(false);
arreter.setEnabled(true);
animated = true;
t = new Thread(new PlayAnimation());
t.start();
}
}
}
class Bouton2Listener implements ActionListener{
public void actionPerformed(ActionEvent e) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous arrêter l'animation ?", "Arrêt de l'animation", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if(option != JOptionPane.NO_OPTION && option != JOptionPane.CANCEL_OPTION && option != JOptionPane.CLOSED_OPTION)
{
animated = false;
//On remplace nos boutons par nos JMenuItem
lancer.setEnabled(true);
arreter.setEnabled(false);
}
}
}
class PlayAnimation implements Runnable{
public void run() {
go();
}
}
class FormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//On commente cette ligne pour l'instant
//****************************************
//pan.setForme(combo.getSelectedItem().toString());
}
}
class MorphListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Si la case est cochée, activation du mode morphing
if(morph.isSelected())pan.setMorph(true);
//Sinon rien !
else pan.setMorph(false);
}
}
}
|
Ce que vous devez obtenir :
Vous
pouvez remarquer que notre IHM est beaucoup plus propre...

Il
ne nous reste plus qu'à faire interagir nos menus avec notre animation ! Rien de
plus simple, il suffit de dire à nos
MenuItem qu'une implémentation les
écoutent. En fait, cela revient à faire comme si nous cliquions sur des boutons,
à l'exception des cases à cocher et des radios où, là, nous pouvons utiliser une
implémentation d'
ActionListener
ou d'
ItemListener : nous utiliserons la
première.
Afin que l'application fonctionne bien, j'ai apporté deux
modifications mineures dans la classe
Panneau :
- ajout d'une instruction dans une condition, celle-ci :
Code : Java
1
2
3
4 |
//J'ai rajouté : || this.forme.equals("CARRÉ")
if(this.forme.equals("CARRE") || this.forme.equals("CARRÉ")){
g.fillRect(posX, posY, 50, 50);
}
|
Pour accepter les deux
orthographe ! 
- ajout d'un toUpperCase() dans
:
Code : Java
1
2
3 |
public void setForme(String form){
this.forme = form.toUpperCase();
}
|
Afin d'être sûr que le test
sur cette chaîne de caractères soit sur des
majuscules...
Voici le code de notre animation avec un beau
menu pour tout contrôler :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JPanel container = new JPanel();
private int compteur = 0;
private boolean animated = true;
private boolean backX, backY;
private int x,y ;
private Thread t;
private JMenuBar menuBar = new JMenuBar();
private JMenu animation = new JMenu("Animation"),
forme = new JMenu("Forme"),
typeForme = new JMenu("Type de forme"),
aPropos = new JMenu("À propos");
private JMenuItem lancer = new JMenuItem("Lancer l'animation"),
arreter = new JMenuItem("Arrêter l'animation"),
quitter = new JMenuItem("Quitter"),
aProposItem = new JMenuItem("?");
private JCheckBoxMenuItem morph = new JCheckBoxMenuItem("Morphing");
private JRadioButtonMenuItem carre = new JRadioButtonMenuItem("Carré"),
rond = new JRadioButtonMenuItem("Rond"),
triangle = new JRadioButtonMenuItem("Triangle"),
etoile = new JRadioButtonMenuItem("Etoile");
private ButtonGroup bg = new ButtonGroup();
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(pan, BorderLayout.CENTER);
this.setContentPane(container);
this.initMenu();
this.setVisible(true);
}
private void initMenu(){
//Menu animation
//****************************
//Ajout du listener pour lancer l'animation
lancer.addActionListener(new StartAnimationListener());
animation.add(lancer);
//Ajout du listener pour arrêter l'animation
arreter.addActionListener(new StopAnimationListener());
arreter.setEnabled(false);
animation.add(arreter);
animation.addSeparator();
quitter.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
System.exit(0);
}
});
animation.add(quitter);
//Menu forme
bg.add(carre);
bg.add(triangle);
bg.add(rond);
bg.add(etoile);
//On crée un nouvel écouteur, inutile de créer 4 instances différentes
FormeListener fl = new FormeListener();
carre.addActionListener(fl);
rond.addActionListener(fl);
triangle.addActionListener(fl);
etoile.addActionListener(fl);
typeForme.add(rond);
typeForme.add(carre);
typeForme.add(triangle);
typeForme.add(etoile);
rond.setSelected(true);
forme.add(typeForme);
//Ajout du listener pour le morphing
morph.addActionListener(new MorphListener());
forme.add(morph);
//menu à propos
//Ajout de ce que doit faire le "?"
aProposItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
ImageIcon img = new ImageIcon("images/cysboy.gif");
String mess = "Merci ! \n J'espère que vous vous amusez bien ! \n";
mess += "Je crois qu'il est temps d'ajouter des accélérateurs et des mnémoniques dans tout ça...\n";
mess += "\n Allez, GO les ZérOs !";
jop.showMessageDialog(null, mess, "À propos", JOptionPane.INFORMATION_MESSAGE, img);
}
});
aPropos.add(aProposItem);
//Ajout des menus dans la barre de menus
menuBar.add(animation);
menuBar.add(forme);
menuBar.add(aPropos);
//Ajouit de la barre de menus sur la fenêtre
this.setJMenuBar(menuBar);
}
private void go(){
x = pan.getPosX();
y = pan.getPosY();
while(this.animated){
//System.out.println("OK");
//Si le mode morphing est activé, on utilise la taille actuelle de la forme
if(pan.isMorph())
{
if(x < 1)backX = false;
if(x > pan.getWidth() - pan.getDrawSize())backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight() - pan.getDrawSize())backY = true;
}
//Sinon, comme d'habitude
else
{
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
}
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Ecouteur du menu Lancer
* @author CHerby
*/
public class StartAnimationListener implements ActionListener{
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous lancer l'animation ?", "Lancement de l'animation", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE);
if(option == JOptionPane.OK_OPTION)
{
lancer.setEnabled(false);
arreter.setEnabled(true);
animated = true;
t = new Thread(new PlayAnimation());
t.start();
}
}
}
/**
* Ecouteur du menu Quitter
* @author CHerby
*/
class StopAnimationListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous arrêter l'animation ?", "Arrêt de l'animation", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if(option != JOptionPane.NO_OPTION && option != JOptionPane.CANCEL_OPTION && option != JOptionPane.CLOSED_OPTION)
{
animated = false;
//On remplace nos boutons par nos JMenuItem
lancer.setEnabled(true);
arreter.setEnabled(false);
}
}
}
/**
* Lance le thread.
* @author CHerby
*/
class PlayAnimation implements Runnable{
public void run() {
go();
}
}
/**
* Ecoute les menus forme
* @author CHerby
*/
class FormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
pan.setForme(((JRadioButtonMenuItem)e.getSource()).getText());
}
}
/**
* Ecoute le menu Morphing
* @author CHerby
*/
class MorphListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Si la case est cochée, activation du mode morphing
if(morph.isSelected())pan.setMorph(true);
//Sinon rien !
else pan.setMorph(false);
}
}
}
|
J'ai modifié le nom des implémentations et
j'ai ajouté mon avatar du SDZ dans le dialogue du menu "À
propos". Mettez une image si vous avez envie...

Comme
je l'ai dit dans le dialogue du menu "
À
propos", je crois qu'il est temps de mettre des raccourcis
clavier dans tout ceci !
Vous êtes prêts ?
Les raccourcis clavier
En fait, ceci est très simple aussi. Pour
ajouter un accélérateur sur un
JMenu, nous appelerons la méthode
setAccelerator();
et pour ajouter un mnémonique sur un
JMenuItem, nous invoquerons la méthode
setMnemonic(); .
Attribuons le mnémonique '
A' pour le menu "
Animation", le mnémonique '
F' pour le menu "
Forme" et enfin '
P' pour "
À
propos". Vous allez voir, c'est très simple : il vous suffit
d'invoquer la méthode
setMnemonic(char mnemonic); sur le
JMenu que vous
désirez.
Ce qui nous donne, dans notre cas :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 |
private void initMenu(){
//Menu animation
//****************************
//Ajout du listener pour lancer l'animation
lancer.addActionListener(new StartAnimationListener());
animation.add(lancer);
//Ajout du listener pour arrêter l'animation
arreter.addActionListener(new StopAnimationListener());
arreter.setEnabled(false);
animation.add(arreter);
animation.addSeparator();
quitter.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
System.exit(0);
}
});
animation.add(quitter);
//Menu forme
bg.add(carre);
bg.add(triangle);
bg.add(rond);
bg.add(etoile);
//On crée un nouvel écouteur, inutile de créer 4 instances différentes
FormeListener fl = new FormeListener();
carre.addActionListener(fl);
rond.addActionListener(fl);
triangle.addActionListener(fl);
etoile.addActionListener(fl);
typeForme.add(rond);
typeForme.add(carre);
typeForme.add(triangle);
typeForme.add(etoile);
rond.setSelected(true);
forme.add(typeForme);
//Ajout du listener pour le morphing
morph.addActionListener(new MorphListener());
forme.add(morph);
//menu à propos
//Ajout de ce que doit faire le "?"
aProposItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
ImageIcon img = new ImageIcon("images/cysboy.gif");
String mess = "Merci ! \n J'espère que vous vous amusez bien ! \n";
mess += "Je crois qu'il est temps d'ajouter des accélérateurs et des mnémoniques dans tout ça...\n";
mess += "\n Allez, GO les ZérOs !";
jop.showMessageDialog(null, mess, "À propos", JOptionPane.INFORMATION_MESSAGE, img);
}
});
aPropos.add(aProposItem);
//Ajout des menus dans la barre de menus ET AJOUT DE MNEMONICS ! !
animation.setMnemonic('A');
menuBar.add(animation);
forme.setMnemonic('F');
menuBar.add(forme);
aPropos.setMnemonic('P');
menuBar.add(aPropos);
//Ajouit de la barre de menus sur la fenêtre
this.setJMenuBar(menuBar);
}
|
Nous avons à présent les
lettres correspondantes soulignées dans nos menus, et mieux encore, si vous
tapez
ALT + <la lettre> : le
menu correspondant se déroule !

Voici
ce que j'obtiens :
Concernant les mnémoniques, vous devez savoir que
vous pouvez aussi en mettre sur les objets JMenuItem.
Il faut que vous sachiez
aussi qu'il existe une autre façon d'ajouter un mnémonique sur un JMenu mais
UNIQUEMENT SUR UN JMenu : en passant le mnémonique en
deuxième paramètre du constructeur de l'objet, comme ceci JMenu menu = new JMenu("Fichier", 'F');//Ici, ce menu aura le
mnémonique F .
Oui, je sais, c'est simple et même
très simple. Pour ajouter des accélérateurs, c'est quasiment pareil sauf que
nous devrons utiliser un nouvel objet :
KeyStroke.
Cet objet permet de
connaître l'identité de la touche utilisée ou à utiliser. De ce fait, c'est
grâce à cet objet que nous allons pouvoir construire des combinaisons de touches
pour nos accélérateurs !
Nous allons commencer par attribuer un simple
caractère comme accélérateur pour notre
JMenuItem "
Lancer", ceci en utilisant la méthode
getKeyStroke(char caracter);
de l'objet
KeyStroke.
Rajoutez cette ligne de
code au début de la méthode
initMenu() :
Code : Java
1
2 |
//Cette instruction ajoute l'accélérateur 'c' à notre objet
lancer.setAccelerator(KeyStroke.getKeyStroke('c'));
|
Vous aurez besoin de ces packages : javax.swing.KeyStroke
et java.awt.event.ActionEvent
Testez votre application et vous devriez vous rendre compte
qu'un petit '
c' est apparu à côté du
menu "
Lancer".Voyez plutôt :
Et,
si vous appuyez sur la touche 'c' de votre clavier, celle-ci a le même effet
qu'un clic sur le menu "
Lancer" !
Attention :
si vous mettez le caractère 'C', vous serez obligés d'appuyer simultanément sur
SHIFT + c ou alors d'avoir la touche
MAJ activée !
Le principe
est bien, cependant, imaginez aussi que, maintenant, votre touche 'c' vous
demandera systématiquement le lancement de votre animation !
C'est l'une des
raisons pour laquelle les accélérateurs sont, en général, des combinaisons de
touches du genre
CTRL + c ou encore
CTRL + SHIFT + S.
Pour réussir
à faire ceci, nous allons utiliser une autre méthode
getKeyStroke()
: celle-ci prendra non pas le caractère de notre touche, mais son code,
ainsi qu'une ou plusieurs touches faisant la combinaison !
Pour obtenir le code d'une touche, nous utiliserons
l'objet KeyEvent, qui stocke tous
les codes de touches !
Dans le code qui suit, je crée un
accélérateur
CTRL + L pour le menu
"
Lancer" et un accélérateur
CTRL + SHIT + A pour le menu
"
Arrêter" :
Code : Java
1
2
3
4
5
6
7
8
9
10 |
lancer.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L,
KeyEvent.CTRL_MASK));
animation.add(lancer);
//Ajout du listener pour arrêter l'animation
arreter.addActionListener(new StopAnimationListener());
arreter.setEnabled(false);
arreter.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
KeyEvent.CTRL_DOWN_MASK + KeyEvent.SHIFT_DOWN_MASK));
animation.add(arreter);
|
Et ceci nous donne :
À
ce moment-ci, j'imagine que vous devez être perturbés par ceci :
KeyEvent.VK_L et les appels du même genre.
En fait, la
classe
KeyEvent répertorie tous les codes de
toutes les touches du clavier. Une grande majorité sont sous la forme
VK_<
le
caractère ou le nom de la touche>, lisez ceci comme :
Value of Key <nom de la touche>.
Mis à part
certaines touches de contrôle comme
CTRL, ALT,
SHIFT... vous pouvez retrouver facilement le code d'une touche
grâce à cet objet !
Ensuite, vous avez dû remarquer que lorsque vous avez
voulu taper
KeyEvent.CTRL_DOWN_MASK , Eclipse vous
propose quasiment la même chose :
Vous
pouvez aisément voir qu'Eclipse vous dit que la version
CTRL_DOWN_MASK est la plus récente et qu'il est
vivement conseillé de l'utiliser !
Maintenant que vous savez comment créer des
mnémoniques et des accélérateurs, mettez-en où vous voulez ! Ceci dépend de
vous...
Vous voilà donc avec un zoli menu avec des mnémoniques et
des accélérateurs.
Il est donc temps de voir comment créer un menu contextuel
!
Faire un menu contextuel
Vous allez vous rendre compte que vous avez déjà fait
le plus dur...
Nous allons seulement utiliser un autre objet : un
JPopupMenu. Dans lequel nous allons mettre
nos
JMenuItem
ou / et
JMenu.
Il va falloir tout de même
dire à notre menu contextuel comment s'afficher et surtout où, mais vous verrez
que c'est très simple...

Maintenant
que vous commencez à bien connaître les principes de bases de la programmation
événementielle, nous allons aller plus vite pour apprendre de nouvelles choses
!
Les points importants pour notre menu contextuel
- Les actions à effectuer, dans le cas d'actions identiques au menu, doivent
être les mêmes : nous devrons donc créer des objets globaux aux deux menus.
- Le menu contextuel ne doit s'afficher que dans la zone où l'animation
s'exécute, et non sur le menu !
- Il ne doit s'afficher que lorsqu'on fait un clic droit, et uniquement ça !
Nous allons mettre, dans notre menu contextuel, les actions
"
Lancer l'animation" et "
Arrêter l'animation" ainsi que deux nouveautés :
- pouvoir changer la couleur du fond de notre animation ;
- avoir la possibilité de changer la couleur de notre forme !
Avant d'implémenter les deux nouvelles fonctionnalités, nous
allons travailler sur les deux premières.
Ce qui signifie que, lorsque nous
lancerons l'animation, nous devrons mettre les deux menus "
Lancer l'animation" à l'état
setEnabled(false); et les deux menus
"
Arrêter l'animation" à l'état
setEnabled(true); , et inversement pour
l'arrêt.
Comme je vous l'ai dit plus haut, nous allons utiliser le même
objet écouteur pour le traitement des deux menus, nous allons devoir créer une
véritable instance de ces objets et avertir que ces objets écoutent non
seulement le menu du haut, mais aussi le menu contextuel.
Nous avons
parfaitement le droit de faire ça :
plusieurs écouteurs peuvent écouter un composant et plusieurs
composants peuvent avoir le même écouteur !Vous
êtes presque prêts à créer votre menu contextuel, il ne vous manque que trois
informations :
- comment dire à notre panneau d'afficher le menu contextuel ? Et où ?
- comment lui spécifier qu'il doit le faire uniquement sur le clic droit ?
Le déclenchement de l'affichage de la popup doit se faire sur
un clic de souris, vous connaissez une interface qui gère ce type d'événement :
l'interface
MouseListener. Nous allons donc dire à
notre panneau qu'un écouteur du type de cet interface va l'écouter !
Quelle méthode doit-on redéfinir ? mouseClicked() ?
Si vous voulez, mais je
pensais plutôt à
mouseReleased(),
pour une raison simple que vous ne devez sûrement pas connaître : ces deux
événements sont quasiment identiques, cependant, dans un certain cas, seul
l'événement
mouseClicked() sera
appelé. Il s'agit du cas où vous cliquez sur une zone, que vous déplacez votre
souris tout en maintenant le clic et que vous relâchez le bouton de la souris
ensuite.
C'est pour cette raison que je préfère utiliser la méthode
mouseReleased(). Ensuite, pour
spécifier où afficher le menu contextuel, la classe
JPopupMenu possède une
méthode
show(Component invoker, int x, int y); .
- Component invoker : désigne l'objet invoquant le menu contextuel,
dans notre cas, notre instance de Panneau.
- int x
: coordonnée X du menu.
- int y
: Coordonnée Y du menu.
Je
suis sûr que vous savez comment vous y prendre pour dire au menu contextuel de
s'afficher, il ne vous manque plus qu'à détecter le clic droit. Et là, l'objet
MouseEvent va vous sauver la mise !
En
effet, cet objet possède une méthode
isPopupTrigger() qui renvoie vrai
s'il s'agit d'un clic droit.
Vous avez tous les éléments en mains pour faire votre menu contextuel,
rappelez-vous que nous ne gérons pas tout de suite les nouvelles
fonctionnalités...
Quelques instants de réflexion... Vous avez fini ?
Nous pouvons comparer nos codes ?
Secret (cliquez
pour afficher)
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.KeyStroke;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JPanel container = new JPanel();
private int compteur = 0;
private boolean animated = true;
private boolean backX, backY;
private int x,y ;
private Thread t;
//***********************************************************
// La déclaration pour le menu de la JMenuBar
//***********************************************************
private JMenuBar menuBar = new JMenuBar();
private JMenu animation = new JMenu("Animation"),
forme = new JMenu("Forme"),
typeForme = new JMenu("Type de forme"),
aPropos = new JMenu("À propos");
private JMenuItem lancer = new JMenuItem("Lancer l'animation"),
arreter = new JMenuItem("Arrêter l'animation"),
quitter = new JMenuItem("Quitter"),
aProposItem = new JMenuItem("?");
private JCheckBoxMenuItem morph = new JCheckBoxMenuItem("Morphing");
private JRadioButtonMenuItem carre = new JRadioButtonMenuItem("Carré"),
rond = new JRadioButtonMenuItem("Rond"),
triangle = new JRadioButtonMenuItem("Triangle"),
etoile = new JRadioButtonMenuItem("Etoile");
private ButtonGroup bg = new ButtonGroup();
//***********************************************************
// La déclaration pour le menu contextuel
//***********************************************************
private JPopupMenu jpm = new JPopupMenu();
private JMenu background = new JMenu("Couleur de fond");
private JMenu couleur = new JMenu("Couleur de la forme");
private JMenuItem launch = new JMenuItem("Lancer l'animation");
private JMenuItem stop = new JMenuItem("Arrêter l'animation");
private JMenuItem rouge = new JMenuItem("Rouge"),
bleu = new JMenuItem("Bleu"),
vert = new JMenuItem("Vert"),
rougeBack = new JMenuItem("Rouge"),
bleuBack = new JMenuItem("Bleu"),
vertBack = new JMenuItem("Vert");
//***********************************************************
// ON CRÉE DES LISTENER GLOBAUX
//***********************************************************
private StopAnimationListener stopAnimation = new StopAnimationListener();
private StartAnimationListener startAnimation = new StartAnimationListener();
//**********************************************************
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
//On initialise le menu stop
stop.setEnabled(false);
//On affecte les écouteurs
stop.addActionListener(stopAnimation);
launch.addActionListener(startAnimation);
//On crée et on passe l'écouteur pour afficher le menu contextuel
//Création d'une implémentation de MouseAdapter
//avec redéfinition de la méthode adéquate
pan.addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent event){
//Seulement s'il s'agit d'un clic droit
if(event.isPopupTrigger())
{
background.add(rougeBack);
background.add(bleuBack);
background.add(vertBack);
couleur.add(rouge);
couleur.add(bleu);
couleur.add(vert);
jpm.add(launch);
jpm.add(stop);
jpm.add(couleur);
jpm.add(background);
//La méthode qui va afficher le menu
jpm.show(pan, event.getX(), event.getY());
}
}
});
container.add(pan, BorderLayout.CENTER);
this.setContentPane(container);
this.initMenu();
this.setVisible(true);
}
private void initMenu(){
//Menu animation
//****************************
//Ajout du listener pour lancer l'animation
//ATTENTION LE LISTENER EST GLOBAL ! ! ! !
//-----------------------------------------------
lancer.addActionListener(startAnimation);
//On attribut l'accélerateur c
lancer.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L,
KeyEvent.CTRL_MASK));
animation.add(lancer);
//Ajout du listener pour arrêter l'animation
//LISTENER A CHANGER ICI AUSSI
//--------------------------------------------
arreter.addActionListener(stopAnimation);
arreter.setEnabled(false);
arreter.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
KeyEvent.CTRL_DOWN_MASK + KeyEvent.SHIFT_DOWN_MASK));
animation.add(arreter);
animation.addSeparator();
quitter.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
System.exit(0);
}
});
animation.add(quitter);
//Menu forme
bg.add(carre);
bg.add(triangle);
bg.add(rond);
bg.add(etoile);
//On crée un nouvel écouteur, inutile de créer 4 instances différentes
FormeListener fl = new FormeListener();
carre.addActionListener(fl);
rond.addActionListener(fl);
triangle.addActionListener(fl);
etoile.addActionListener(fl);
typeForme.add(rond);
typeForme.add(carre);
typeForme.add(triangle);
typeForme.add(etoile);
rond.setSelected(true);
forme.add(typeForme);
//Ajout du listener pour le morphing
morph.addActionListener(new MorphListener());
forme.add(morph);
//menu à propos
//Ajout de ce que doit faire le "?"
aProposItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
ImageIcon img = new ImageIcon("images/cysboy.gif");
String mess = "Merci ! \n J'espère que vous vous amusez bien ! \n";
mess += "Je crois qu'il est temps d'ajouter des accélérateurs et des mnémoniques dans tout ça...\n";
mess += "\n Allez, GO les ZérOs !";
jop.showMessageDialog(null, mess, "À propos", JOptionPane.INFORMATION_MESSAGE, img);
}
});
aPropos.add(aProposItem);
//Ajout des menus dans la barre de menus
animation.setMnemonic('A');
menuBar.add(animation);
forme.setMnemonic('F');
menuBar.add(forme);
aPropos.setMnemonic('P');
menuBar.add(aPropos);
//Ajouit de la barre de menus sur la fenêtre
this.setJMenuBar(menuBar);
}
private void go(){
x = pan.getPosX();
y = pan.getPosY();
while(this.animated){
//System.out.println("OK");
//Si le mode morphing est activé, on utilise la taille actuelle de la forme
if(pan.isMorph())
{
if(x < 1)backX = false;
if(x > pan.getWidth() - pan.getDrawSize())backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight() - pan.getDrawSize())backY = true;
}
//Sinon, comme d'habitude
else
{
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
}
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Écouteur du menu Lancer
* @author CHerby
*/
public class StartAnimationListener implements ActionListener{
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous lancer l'animation ?", "Lancement de l'animation", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE);
if(option == JOptionPane.OK_OPTION)
{
lancer.setEnabled(false);
arreter.setEnabled(true);
//ON AJOUTE L'INSTRUCTION POUR LE MENU CONTEXTUEL
//************************************************
launch.setEnabled(false);
stop.setEnabled(true);
animated = true;
t = new Thread(new PlayAnimation());
t.start();
}
}
}
/**
* Écouteur du menu Quitter
* @author CHerby
*/
class StopAnimationListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous arrêter l'animation ?", "Arrêt de l'animation", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if(option != JOptionPane.NO_OPTION && option != JOptionPane.CANCEL_OPTION && option != JOptionPane.CLOSED_OPTION)
{
animated = false;
//On remplace nos boutons par nos JMenuItem
lancer.setEnabled(true);
arreter.setEnabled(false);
//ON AJOUTE L'INSTRUCTION POUR LE MENU CONTEXTUEL
//************************************************
launch.setEnabled(true);
stop.setEnabled(false);
}
}
}
/**
* Lance le thread.
* @author CHerby
*/
class PlayAnimation implements Runnable{
public void run() {
go();
}
}
/**
* Écoute les menus forme
* @author CHerby
*/
class FormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
pan.setForme(((JRadioButtonMenuItem)e.getSource()).getText());
}
}
/**
* Écoute le menu Morphing
* @author CHerby
*/
class MorphListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Si la case est cochée, activation du mode morphing
if(morph.isSelected())pan.setMorph(true);
//Sinon rien !
else pan.setMorph(false);
}
}
}
|
Voici ce que
j'obtiens :
Victoire
!
Il est beau, il est fonctionnel, il est zérotissime notre menu contextuel
!
Je sens que vous êtes prêts pour les nouvelles fonctionnalités... Même
si je me doute que certains d'entre vous ont déjà fait ce qu'il fallait.

Il
n'est pas très difficile de faire ce genre de chose, surtout que vous êtes
habitués, maintenant.
Dans notre classe
Panneau, nous utilisons des couleurs
définies à l'avance, il nous suffit donc de mettre ces couleurs dans des
variables et de permettre l'édition de celles-ci.
Rien de difficile ici,
voici donc les codes sources de nos deux classes :
Panneau.java
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 |
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class Panneau extends JPanel {
private int posX = -50;
private int posY = -50;
private int drawSize = 50;
//boolean pour le mode morphing et pour savoir si la taille doit être réduite
private boolean morph = false, reduce = false;;
private String forme = "ROND";
//***********************************
//Voici nos deux couleurs
//***********************************
private Color couleurForme = Color.red;
private Color couleurFond = Color.white;
//Le compteur de rafraîchissement
private int increment = 0;
public void paintComponent(Graphics g){
//affectation de la couleur de fond
g.setColor(couleurFond);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
//Affectation de la couleur de la forme
g.setColor(couleurForme);
//Si le mode morphing est activé, on peint le morphing
if(this.morph)
drawMorph(g);
//sinon, mode normal
else
draw(g);
}
/**
* Méthode qui redéfinit la couleur du fond
* @param color
*/
public void setCouleurFond(Color color){
this.couleurFond = color;
}
/**
* Méthode qui redéfinit la couleur de la forme
* @param color
*/
public void setCouleurForme(Color color){
this.couleurForme = color;
}
private void draw(Graphics g){
if(this.forme.equals("ROND")){
g.fillOval(posX, posY, 50, 50);
}
if(this.forme.equals("CARRE") || this.forme.equals("CARRÉ")){
g.fillRect(posX, posY, 50, 50);
}
if(this.forme.equals("TRIANGLE")){
int s1X = posX + 50/2;
int s1Y = posY;
int s2X = posX + 50;
int s2Y = posY + 50;
int s3X = posX;
int s3Y = posY + 50;
int[] ptsX = {s1X, s2X, s3X};
int[] ptsY = {s1Y, s2Y, s3Y};
g.fillPolygon(ptsX, ptsY, 3);
}
if(this.forme.equals("ETOILE")){
int s1X = posX + 50/2;
int s1Y = posY;
int s2X = posX + 50;
int s2Y = posY + 50;
g.drawLine(s1X, s1Y, s2X, s2Y);
int s3X = posX;
int s3Y = posY + 50/3;
g.drawLine(s2X, s2Y, s3X, s3Y);
int s4X = posX + 50;
int s4Y = posY + 50/3;
g.drawLine(s3X, s3Y, s4X, s4Y);
int s5X = posX;
int s5Y = posY + 50;
g.drawLine(s4X, s4Y, s5X, s5Y);
g.drawLine(s5X, s5Y, s1X, s1Y);
}
}
/**
* Méthode qui peint le morphing
* @param g
*/
private void drawMorph(Graphics g){
//On incrémente le tour
increment++;
//On regarde si on doit réduire ou non
if(drawSize >= 50)reduce = true;
if(drawSize <= 10)reduce = false;
if(reduce)
drawSize = drawSize - getUsedSize();
else
drawSize = drawSize + getUsedSize();
if(this.forme.equals("ROND")){
g.fillOval(posX, posY, drawSize, drawSize);
}
if(this.forme.equals("CARRE")){
g.fillRect(posX, posY, drawSize, drawSize);
}
if(this.forme.equals("TRIANGLE")){
int s1X = posX + drawSize/2;
int s1Y = posY;
int s2X = posX + drawSize;
int s2Y = posY + drawSize;
int s3X = posX;
int s3Y = posY + drawSize;
int[] ptsX = {s1X, s2X, s3X};
int[] ptsY = {s1Y, s2Y, s3Y};
g.fillPolygon(ptsX, ptsY, 3);
}
if(this.forme.equals("ETOILE")){
int s1X = posX + drawSize/2;
int s1Y = posY;
int s2X = posX + drawSize;
int s2Y = posY + drawSize;
g.drawLine(s1X, s1Y, s2X, s2Y);
int s3X = posX;
int s3Y = posY + drawSize/3;
g.drawLine(s2X, s2Y, s3X, s3Y);
int s4X = posX + drawSize;
int s4Y = posY + drawSize/3;
g.drawLine(s3X, s3Y, s4X, s4Y);
int s5X = posX;
int s5Y = posY + drawSize;
g.drawLine(s4X, s4Y, s5X, s5Y);
g.drawLine(s5X, s5Y, s1X, s1Y);
}
}
/**
* Méthode qui retourne le nombre à retrancher ou ajouter pour le morphing
* @return res
*/
private int getUsedSize(){
int res = 0;
//Si le nombre de tours est 10
//On réinitialise l'incrément et on retourne 1
if(increment / 10 == 1){
increment = 0;
res = 1;
}
return res;
}
public int getDrawSize(){
return drawSize;
}
public boolean isMorph(){
return morph;
}
public void setMorph(boolean bool){
this.morph = bool;
//On réinitialise la taille
drawSize = 50;
}
public void setForme(String form){
this.forme = form.toUpperCase();
}
public int getPosX() {
return posX;
}
public void setPosX(int posX) {
this.posX = posX;
}
public int getPosY() {
return posY;
}
public void setPosY(int posY) {
this.posY = posY;
}
}
|
Fenetre.java
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.KeyStroke;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JPanel container = new JPanel();
private int compteur = 0;
private boolean animated = true;
private boolean backX, backY;
private int x,y ;
private Thread t;
//***********************************************************
// La déclaration pour le menu de la JMenuBar
//***********************************************************
private JMenuBar menuBar = new JMenuBar();
private JMenu animation = new JMenu("Animation"),
forme = new JMenu("Forme"),
typeForme = new JMenu("Type de forme"),
aPropos = new JMenu("À propos");
private JMenuItem lancer = new JMenuItem("Lancer l'animation"),
arreter = new JMenuItem("Arrêter l'animation"),
quitter = new JMenuItem("Quitter"),
aProposItem = new JMenuItem("?");
private JCheckBoxMenuItem morph = new JCheckBoxMenuItem("Morphing");
private JRadioButtonMenuItem carre = new JRadioButtonMenuItem("Carré"),
rond = new JRadioButtonMenuItem("Rond"),
triangle = new JRadioButtonMenuItem("Triangle"),
etoile = new JRadioButtonMenuItem("Etoile");
private ButtonGroup bg = new ButtonGroup();
//***********************************************************
// La déclaration pour le menu contextuel
//***********************************************************
private JPopupMenu jpm = new JPopupMenu();
private JMenu background = new JMenu("Couleur de fond");
private JMenu couleur = new JMenu("Couleur de la forme");
private JMenuItem launch = new JMenuItem("Lancer l'animation");
private JMenuItem stop = new JMenuItem("Arrêter l'animation");
private JMenuItem rouge = new JMenuItem("Rouge"),
bleu = new JMenuItem("Bleu"),
vert = new JMenuItem("Vert"),
blanc = new JMenuItem("Blanc"),
rougeBack = new JMenuItem("Rouge"),
bleuBack = new JMenuItem("Bleu"),
vertBack = new JMenuItem("Vert"),
blancBack = new JMenuItem("Blanc");
//***********************************************************
// ON CRÉE DES LISTENER GLOBAUX
//***********************************************************
private StopAnimationListener stopAnimation = new StopAnimationListener();
private StartAnimationListener startAnimation = new StartAnimationListener();
private CouleurFondListener bgColor = new CouleurFondListener();
private CouleurFormeListener frmColor = new CouleurFormeListener();
//**********************************************************
public Fenetre(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
//On initialise le menu stop
stop.setEnabled(false);
//On affecte les écouteurs
stop.addActionListener(stopAnimation);
launch.addActionListener(startAnimation);
//On affecte les écouteurs aux points de menu
rouge.addActionListener(frmColor);
bleu.addActionListener(frmColor);
vert.addActionListener(frmColor);
blanc.addActionListener(frmColor);
rougeBack.addActionListener(bgColor);
bleuBack.addActionListener(bgColor);
vertBack.addActionListener(bgColor);
blancBack.addActionListener(bgColor);
//On crée et on passe l'écouteur pour afficher le menu contextuel
//Création d'une implémentation de MouseAdapter
//avec redéfinition de la méthode adéquate
pan.addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent event){
//Seulement s'il s'agit d'un clic droit
if(event.isPopupTrigger())
{
background.add(blancBack);
background.add(rougeBack);
background.add(bleuBack);
background.add(vertBack);
couleur.add(blanc);
couleur.add(rouge);
couleur.add(bleu);
couleur.add(vert);
jpm.add(launch);
jpm.add(stop);
jpm.add(couleur);
jpm.add(background);
//La méthode qui va afficher le menu
jpm.show(pan, event.getX(), event.getY());
}
}
});
container.add(pan, BorderLayout.CENTER);
this.setContentPane(container);
this.initMenu();
this.setVisible(true);
}
private void initMenu(){
//Menu animation
//****************************
//Ajout du listener pour lancer l'animation
//ATTENTION LE LISTENER EST GLOBAL ! ! ! !
//-----------------------------------------------
lancer.addActionListener(startAnimation);
//On attribut l'accélerateur c
lancer.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L,
KeyEvent.CTRL_MASK));
animation.add(lancer);
//Ajout du listener pour arrêter l'animation
//LISTENER À CHANGER ICI AUSSI
//--------------------------------------------
arreter.addActionListener(stopAnimation);
arreter.setEnabled(false);
arreter.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
KeyEvent.CTRL_DOWN_MASK + KeyEvent.SHIFT_DOWN_MASK));
animation.add(arreter);
animation.addSeparator();
quitter.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
System.exit(0);
}
});
animation.add(quitter);
//Menu forme
bg.add(carre);
bg.add(triangle);
bg.add(rond);
bg.add(etoile);
//On crée un nouvel écouteur, inutile de créer 4 instances différentes
FormeListener fl = new FormeListener();
carre.addActionListener(fl);
rond.addActionListener(fl);
triangle.addActionListener(fl);
etoile.addActionListener(fl);
typeForme.add(rond);
typeForme.add(carre);
typeForme.add(triangle);
typeForme.add(etoile);
rond.setSelected(true);
forme.add(typeForme);
//Ajout du listener pour le morphing
morph.addActionListener(new MorphListener());
forme.add(morph);
//menu à propos
//Ajout de ce que doit faire le "?"
aProposItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
ImageIcon img = new ImageIcon("images/cysboy.gif");
String mess = "Merci ! \n J'espère que vous vous amusez bien ! \n";
mess += "Je crois qu'il est temps d'ajouter des accélérateurs et des mnémoniques dans tout ça...\n";
mess += "\n Allez, GO les ZérOs !";
jop.showMessageDialog(null, mess, "À propos", JOptionPane.INFORMATION_MESSAGE, img);
}
});
aPropos.add(aProposItem);
//Ajout des menus dans la barre de menus
animation.setMnemonic('A');
menuBar.add(animation);
forme.setMnemonic('F');
menuBar.add(forme);
aPropos.setMnemonic('P');
menuBar.add(aPropos);
//Ajout de la barre de menus sur la fenêtre
this.setJMenuBar(menuBar);
}
private void go(){
x = pan.getPosX();
y = pan.getPosY();
while(this.animated){
//System.out.println("OK");
//Si le mode morphing est activé, on utilise la taille actuelle de la forme
if(pan.isMorph())
{
if(x < 1)backX = false;
if(x > pan.getWidth() - pan.getDrawSize())backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight() - pan.getDrawSize())backY = true;
}
//Sinon, comme d'habitude
else
{
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
}
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Écouteur du menu Lancer
* @author
*/
public class StartAnimationListener implements ActionListener{
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous lancer l'animation ?", "Lancement de l'animation", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE);
if(option == JOptionPane.OK_OPTION)
{
lancer.setEnabled(false);
arreter.setEnabled(true);
//ON AJOUTE L'INSTRUCTION POUR LE MENU CONTEXTUEL
//************************************************
launch.setEnabled(false);
stop.setEnabled(true);
animated = true;
t = new Thread(new PlayAnimation());
t.start();
}
}
}
/**
* Écouteur du menu Quitter
* @author CHerby
*/
class StopAnimationListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous arrêter l'animation ?", "Arrêt de l'animation", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if(option != JOptionPane.NO_OPTION && option != JOptionPane.CANCEL_OPTION && option != JOptionPane.CLOSED_OPTION)
{
animated = false;
//On remplace nos boutons par nos JMenuItem
lancer.setEnabled(true);
arreter.setEnabled(false);
//ON AJOUTE L'INSTRUCTION POUR LE MENU CONTEXTUEL
//************************************************
launch.setEnabled(true);
stop.setEnabled(false);
}
}
}
/**
* Lance le thread.
* @author CHerby
*/
class PlayAnimation implements Runnable{
public void run() {
go();
}
}
/**
* Écoute les menus forme
* @author CHerby
*/
class FormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
pan.setForme(((JRadioButtonMenuItem)e.getSource()).getText());
}
}
/**
* Écoute le menu Morphing
* @author CHerby
*/
class MorphListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Si la case est cochée, activation du mode morphing
if(morph.isSelected())pan.setMorph(true);
//Sinon rien !
else pan.setMorph(false);
}
}
//************************************************************
// CLASSE QUI VONT ÉCOUTER LE CHANGEMENT DE COULEURS
//************************************************************
/**
* Écoute le changement de couleur du fond
*/
class CouleurFondListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == vertBack)
pan.setCouleurFond(Color.green);
else if (e.getSource() == bleuBack)
pan.setCouleurFond(Color.blue);
else if(e.getSource() == rougeBack)
pan.setCouleurFond(Color.red);
else
pan.setCouleurFond(Color.white);
}
}
/**
* Écoute le changement de couleur du fond
*/
class CouleurFormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == vert)
pan.setCouleurForme(Color.green);
else if (e.getSource() == bleu)
pan.setCouleurForme(Color.blue);
else if(e.getSource() == rouge)
pan.setCouleurForme(Color.red);
else
pan.setCouleurForme(Color.white);
}
}
}
|
Et voici quelques résultats
obtenus :
Vous
constatez que les menus et les menus contextuels peuvent être très utiles et
très ergonomiques !
Ils sont relativement simples à utiliser et à
implémenter.
Mais vous aurez sans doute remarqué qu'il y a toujours un clic
superflu, qu'il s'agisse d'un menu ou d'un menu contextuel : il faut au moins un
clic pour afficher le contenu d'un menu (sauf cas avec accélérateur).
Pour contrer ce genre de chose, il existe un outil très puissant : la
barre d'outils !
Les barres d'outils
Voici un exemple de barre d'outils :
Vous
voyez en rose la barre de menus et en vert la barre d'outils.
En fait, pour
faire simple, la barre d'outils sert à effectuer des actions présentes dans le
menu mais sans avoir à chercher dans celui-ci ou à mémoriser le raccourci
clavier (accélérateur). Elle permet donc de pouvoir faire des actions
rapides.
Elle est généralement composée de multitudes de boutons sur
lesquels est apposée une image symbolisant l'action que ce bouton peut
effectuer.
Pour créer et utiliser une barre d'outils, nous allons
utiliser l'objet
JToolBar.
Je vous rassure tout de
suite, cet objet fonctionne comme un menu classique, à la différence près que
celui-ci prend des boutons (
JButton).
Il n'y a pas d'endroit spécifique pour incorporer votre
barre d'outils, il faudra l'expliciter lors de sa création !
Il
nous faut, tout d'abord, des images à mettre sur nos boutons... J'en ai fait de
toutes simples :
Vous
devez avoir une idée de ce que nous allons mettre dans notre barre d'outils...

Concernant
les actions à gérer, pour le lancement de l'animation et l'arrêt, il faudra
penser à éditer le comportement des boutons de la barre d'outils, comme fait
pour les deux actions du menu contextuel...
Concernant les boutons pour
les formes, c'est un peu plus délicat.
Les autres composants pouvant éditer
la forme de notre animation étaient des boutons radios. Or, ici, nous avons des
boutons standards.
Hormis le fait qu'il va falloir une instance précise de la
classe
FormeListener, nous allons devoir modifier
un peu son comportement...
Nous devrons savoir si l'action vient d'un
bouton radio du menu ou d'un bouton de la barre d'outils...
Ça m'a l'air compliqué, tout ça !

En
fait, non, et ceci grâce à l'objet
ActionEvent. Nous allons pouvoir connaître
le déclencheur de l'action ! Nous n'allons pas tester tous les boutons radio un
par un... Le système utilisé jusque là, pour ces composants, était très
bien.
Non, nous allons juste voir si celui qui a déclenché l'action est un
JRadioButtonMenuItem ou non et, dans ce
cas, nous testerons nos boutons...
Comment savoir ça ?
Rappelez-vous le
chapitre sur la réflexivité !La méthode
getSource()
nous retourne un objet, il est donc possible de connaître sa classe avec
la méthode
getClass() et donc, le nom de celle-ci avec la méthode
getName()
...
Il va falloir qu'on pense à mettre à jour le bouton
radio sélectionné dans le menu, alors ?
Tout à fait !
Je vois que
vous commencé à penser événementiel !

Et
là, pour votre plus grand plaisir, j'ai une astuce qui ne marche pas mal du tout
: il suffit, lors du clic sur un bouton de la barre d'outils, de déclencher
l'événement sur le bouton radio correspondant !
On serait curieux de savoir comment tu fais ça !
Dans la classe
AbstractButton, dont héritent tous les
boutons, il y a la méthode
doClick() .
Cette méthode
déclenche un événement identique à un vrai clic de souris sur le composant !
Donc, plutôt que de gérer la même façon de faire à deux endroits, nous allons
rediriger l'action sur un autre composant...

Vous
avez toutes les cartes pour faire votre barre d'outils.
N'oubliez pas que
vous devez spécifier sa place sur le conteneur principal !
Bon, faites
des tests, comparez, codez, effacez mais, au final, vous devriez avoir quelque
chose comme ça :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
public class Fenetre extends JFrame{
private Panneau pan = new Panneau();
private JPanel container = new JPanel();
private int compteur = 0;
private boolean animated = true;
private boolean backX, backY;
private int x,y ;
private Thread t;
//***********************************************************
// La déclaration pour le menu de la JMenuBar
//***********************************************************
private JMenuBar menuBar = new JMenuBar();
private JMenu animation = new JMenu("Animation"),
forme = new JMenu("Forme"),
typeForme = new JMenu("Type de forme"),
aPropos = new JMenu("À propos");
private JMenuItem lancer = new JMenuItem("Lancer l'animation"),
arreter = new JMenuItem("Arrêter l'animation"),
quitter = new JMenuItem("Quitter"),
aProposItem = new JMenuItem("?");
private JCheckBoxMenuItem morph = new JCheckBoxMenuItem("Morphing");
private JRadioButtonMenuItem carre = new JRadioButtonMenuItem("Carré"),
rond = new JRadioButtonMenuItem("Rond"),
triangle = new JRadioButtonMenuItem("Triangle"),
etoile = new JRadioButtonMenuItem("Etoile");
private ButtonGroup bg = new ButtonGroup();
//***********************************************************
// La déclaration pour le menu contextuel
//***********************************************************
private JPopupMenu jpm = new JPopupMenu();
private JMenu background = new JMenu("Couleur de fond");
private JMenu couleur = new JMenu("Couleur de la forme");
private JMenuItem launch = new JMenuItem("Lancer l'animation");
private JMenuItem stop = new JMenuItem("Arrêter l'animation");
private JMenuItem rouge = new JMenuItem("Rouge"),
bleu = new JMenuItem("Bleu"),
vert = new JMenuItem("Vert"),
blanc = new JMenuItem("Blanc"),
rougeBack = new JMenuItem("Rouge"),
bleuBack = new JMenuItem("Bleu"),
vertBack = new JMenuItem("Vert"),
blancBack = new JMenuItem("Blanc");
//***********************************************************
// ON CRÉE DES LISTENER GLOBAUX
//***********************************************************
private StopAnimationListener stopAnimation = new StopAnimationListener();
private StartAnimationListener startAnimation = new StartAnimationListener();
private CouleurFondListener bgColor = new CouleurFondListener();
private CouleurFormeListener frmColor = new CouleurFormeListener();
//**********************************************************
//Création de notre barre d'outils
private JToolBar toolBar = new JToolBar();
//Les boutons
private JButton play = new JButton(new ImageIcon("images/play.jpg")),
cancel = new JButton(new ImageIcon("images/stop.jpg")),
square = new JButton(new ImageIcon("images/carré.jpg")),
tri = new JButton(new ImageIcon("images/triangle.jpg")),
circle = new JButton(new ImageIcon("images/rond.jpg")),
star = new JButton(new ImageIcon("images/étoile.jpg"));
private Color fondBouton = Color.white;
private FormeListener fListener = new FormeListener();
public Fenetre(){
this.setTitle("Animation");
this.setSize(400, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
//On initialise le menu stop
stop.setEnabled(false);
//On affecte les écouteurs
stop.addActionListener(stopAnimation);
launch.addActionListener(startAnimation);
//On affecte les écouteurs aux points de menus
rouge.addActionListener(frmColor);
bleu.addActionListener(frmColor);
vert.addActionListener(frmColor);
blanc.addActionListener(frmColor);
rougeBack.addActionListener(bgColor);
bleuBack.addActionListener(bgColor);
vertBack.addActionListener(bgColor);
blancBack.addActionListener(bgColor);
//On crée et on passe l'écouteur pour afficher le menu contextuel
//Création d'une implémentation de MouseAdapter
//avec redéfinition de la méthode adéquate
pan.addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent event){
//Seulement s'il s'agit d'un clic droit
if(event.isPopupTrigger())
{
background.add(blancBack);
background.add(rougeBack);
background.add(bleuBack);
background.add(vertBack);
couleur.add(blanc);
couleur.add(rouge);
couleur.add(bleu);
couleur.add(vert);
jpm.add(launch);
jpm.add(stop);
jpm.add(couleur);
jpm.add(background);
//La méthode qui va afficher le menu
jpm.show(pan, event.getX(), event.getY());
}
}
});
container.add(pan, BorderLayout.CENTER);
this.setContentPane(container);
this.initMenu();
this.initToolBar();
this.setVisible(true);
}
private void initToolBar(){
this.cancel.setEnabled(false);
this.cancel.addActionListener(stopAnimation);
this.cancel.setBackground(fondBouton);
this.play.addActionListener(startAnimation);
this.play.setBackground(fondBouton);
this.toolBar.add(play);
this.toolBar.add(cancel);
this.toolBar.addSeparator();
//Ajout des Listeners
this.circle.addActionListener(fListener);
this.circle.setBackground(fondBouton);
this.toolBar.add(circle);
this.square.addActionListener(fListener);
this.square.setBackground(fondBouton);
this.toolBar.add(square);
this.tri.setBackground(fondBouton);
this.tri.addActionListener(fListener);
this.toolBar.add(tri);
this.star.setBackground(fondBouton);
this.star.addActionListener(fListener);
this.toolBar.add(star);
this.add(toolBar, BorderLayout.NORTH);
}
private void initMenu(){
//Menu animation
//****************************
//Ajout du listener pour lancer l'animation
//ATTENTION LE LISTENER EST GLOBAL ! ! ! !
//-----------------------------------------------
lancer.addActionListener(startAnimation);
//On attribue l'accélerateur c
lancer.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L,
KeyEvent.CTRL_MASK));
animation.add(lancer);
//Ajout du listener pour arrêter l'animation
//LISTENER À CHANGER ICI AUSSI
//--------------------------------------------
arreter.addActionListener(stopAnimation);
arreter.setEnabled(false);
arreter.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
KeyEvent.CTRL_DOWN_MASK + KeyEvent.SHIFT_DOWN_MASK));
animation.add(arreter);
animation.addSeparator();
quitter.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
System.exit(0);
}
});
animation.add(quitter);
//Menu forme
bg.add(carre);
bg.add(triangle);
bg.add(rond);
bg.add(etoile);
//On crée un nouvel écouteur, inutile de créer 4 instances différentes
carre.addActionListener(fListener);
rond.addActionListener(fListener);
triangle.addActionListener(fListener);
etoile.addActionListener(fListener);
typeForme.add(rond);
typeForme.add(carre);
typeForme.add(triangle);
typeForme.add(etoile);
rond.setSelected(true);
forme.add(typeForme);
//Ajout du listener pour le morphing
morph.addActionListener(new MorphListener());
forme.add(morph);
//menu à propos
//Ajout de ce que doit faire le "?"
aProposItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
ImageIcon img = new ImageIcon("images/cysboy.gif");
String mess = "Merci ! \n J'espère que vous vous amusez bien ! \n";
mess += "Je crois qu'il est temps d'ajouter des accélérateurs et des mnémoniques dans tout ça...\n";
mess += "\n Allez, GO les ZérOs !";
jop.showMessageDialog(null, mess, "À propos", JOptionPane.INFORMATION_MESSAGE, img);
}
});
aPropos.add(aProposItem);
//Ajout des menus dans la barre de menus
animation.setMnemonic('A');
menuBar.add(animation);
forme.setMnemonic('F');
menuBar.add(forme);
aPropos.setMnemonic('P');
menuBar.add(aPropos);
//Ajout de la barre de menus sur la fenêtre
this.setJMenuBar(menuBar);
}
private void go(){
x = pan.getPosX();
y = pan.getPosY();
while(this.animated){
//System.out.println("OK");
//Si le mode morphing est activé, on utilise la taille actuelle de la forme
if(pan.isMorph())
{
if(x < 1)backX = false;
if(x > pan.getWidth() - pan.getDrawSize())backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight() - pan.getDrawSize())backY = true;
}
//Sinon, comme d'habitude
else
{
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
}
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Écouteur du menu Lancer
* @author CHerby
*/
public class StartAnimationListener implements ActionListener{
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous lancer l'animation ?", "Lancement de l'animation", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE);
if(option == JOptionPane.OK_OPTION)
{
lancer.setEnabled(false);
arreter.setEnabled(true);
//ON AJOUTE L'INSTRUCTION POUR LE MENU CONTEXTUEL
//************************************************
launch.setEnabled(false);
stop.setEnabled(true);
play.setEnabled(false);
cancel.setEnabled(true);
animated = true;
t = new Thread(new PlayAnimation());
t.start();
}
}
}
/**
* Écouteur du menu Quitter
* @author CHerby
*/
class StopAnimationListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous arrêter l'animation ?", "Arrêt de l'animation", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if(option != JOptionPane.NO_OPTION && option != JOptionPane.CANCEL_OPTION && option != JOptionPane.CLOSED_OPTION)
{
animated = false;
//On remplace nos bouton par nous MenuItem
lancer.setEnabled(true);
arreter.setEnabled(false);
//ON AJOUTE L'INSTRUCTION POUR LE MENU CONTEXTUEL
//************************************************
launch.setEnabled(true);
stop.setEnabled(false);
play.setEnabled(true);
cancel.setEnabled(false);
}
}
}
/**
* Lance le thread.
* @author CHerby
*/
class PlayAnimation implements Runnable{
public void run() {
go();
}
}
/**
* Écoute les menus forme
* @author CHerby
*/
class FormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Si l'action vient d'un bouton radio du menu
if(e.getSource().getClass().getName().equals("javax.swing.JRadioButtonMenuItem"))
pan.setForme(((JRadioButtonMenuItem)e.getSource()).getText());
else{
if(e.getSource() == square){
carre.doClick();
}
else if(e.getSource() == tri){
triangle.doClick();
}
else if(e.getSource() == star){
etoile.doClick();
}
else{
rond.doClick();
}
}
}
}
/**
* Écoute le menu Morphing
* @author CHerby
*/
class MorphListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Si la case est cochée, activation du mode morphing
if(morph.isSelected())pan.setMorph(true);
//Sinon rien !
else pan.setMorph(false);
}
}
//************************************************************
// CLASSES QUI VONT ECOUTER LE CHANGEMENT DE COULEURS
//************************************************************
/**
* Écoute le changement de couleur du fond
*/
class CouleurFondListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == vertBack)
pan.setCouleurFond(Color.green);
else if (e.getSource() == bleuBack)
pan.setCouleurFond(Color.blue);
else if(e.getSource() == rougeBack)
pan.setCouleurFond(Color.red);
else
pan.setCouleurFond(Color.white);
}
}
/**
* Écoute le changement de couleur du fond
*/
class CouleurFormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == vert)
pan.setCouleurForme(Color.green);
else if (e.getSource() == bleu)
pan.setCouleurForme(Color.blue);
else if(e.getSource() == rouge)
pan.setCouleurForme(Color.red);
else
pan.setCouleurForme(Color.white);
}
}
}
|
Vous devez obtenir une IHM
ressemblant à ceci :
Elle
n'est pas zolie, votre IHM, maintenant ?
Vous avez bien travaillé, surtout
que je vous explique les grandes lignes mais vous avez une part de réflexion, à
présent !
Eh oui, vous avez appris à penser en orienté objet, vous connaissez
les grandes lignes de la programmation événementielle. Maintenant, ce ne sont
juste que des points techniques spécifiques à acquérir comme l'utilisation de
certains objets !
D'ailleurs, vous devez savoir une dernière
chose : dans les barres d'outils comme celle-ci, vous pouvez mettre d'autres
composants que des boutons (combo...).
Pour ceux qui l'auraient
remarqué, la barre d'outils est déplaçable ! Si vous cliquez sur cette zone
:
si
vous laissez le clic appuyé et faites glisser votre souris vers la droite, la
gauche ou encore le bas, vous pourrez voir un carré se déplacer :
Et
lorsque vous relâchez les clics, votre barre à changé d'emplacement :
Elles
sont fortes ces barres d'outils, tout de même !
Par contre, vous devez savoir
que vous pouvez utiliser autre chose qu'un composant sur une barre d'outils...
Utiliser les actions abstraites
Nous avons vu, plus haut, comment centraliser les
actions sur différents composants.
Vous devez savoir qu'il existe une classe
abstraite qui permet de gérer ce genre de chose car elle peut s'adapter à
beaucoup de composants (en général à ceux qui ne font qu'une action comme un
bouton, une case à cocher et non une liste...).
Le but de cette classe
est d'attribuer automatiquement une action à un ou plusieurs composants. Le
principal avantage de cette façon de faire réside dans le fait que plusieurs
composants travaillent avec une implémentation de la classe
AbstractAction, mais le
gros inconvénient réside dans le fait que vous devrez programmer une
implémentation par action :
- une action pour la couleur de la forme en rouge ;
- une action pour la couleur de la forme en bleu ;
- une action pour la couleur de la forme en vert ;
- une action pour la couleur du fond en rouge ;
- une action pour la couleur du fond en bleu ;
- ...
Ceci peut être très lourd à faire, mais après, je vous
laisse juges d'utiliser telle ou telle façon selon vos besoins !
Voici
comment s'implémente cette classe :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 |
public class Fenetre extends JFrame{
//Nous pouvons utiliser les actions abstraites directement dans un JButton
private JButton bouton1 = new JButton(new RougeAction("ActionRouge", new ImageIcon("images/rouge.jpg"));
//Ou créer un instance concrète
private RougeAction rAct = new RougeAction("ActionRouge", new ImageIcon("images/rouge.jpg"));
private JToolBar toolBar = new JToolBar();
//...
//Et le plus fort : UTILISER UNE INSTANCE D'ACTION DIRECTEMENT DANS UNE BARRE D'OUTILS
private void initToolBar(){
toolBar.add(rAct);
}
//...
class RougeAction extends AbstractAction{
//Constructeur avec le nom uniquement
public RougeAction(String name){super(name);}
//Le constructeur prend le nom et une icône en paramètre
public RougeAction(String name, ImageIcon){super(name, img);}
public void actionPerformed(ActionEvent){
//Vous connaissez la marche à suivre
}
}
}
|
Vous pouvez voir que cela
peut être très pratique. De plus, si vous ajoutez une action sur une barre
d'outils, celle-ci crée automatiquement un bouton correspondant !

Ensuite,
le choix d'utiliser les actions abstraites ou des implémentations de telle ou
telle interface vous revient.
Nous pouvons d'ailleurs très bien
appliquer ce principe au code de notre animation.
Voici ce que peut
donner le code vu précédemment avec des implémentations de la classe
AbstractAction :
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
public class ZFenetre extends JFrame{
private Panneau pan = new Panneau();
private JPanel container = new JPanel();
private int compteur = 0;
private boolean animated = true;
private boolean backX, backY;
private int x,y ;
private Thread t;
//Création des instances d'actions de forme
private CarreAction cAction = new CarreAction("Carré", new ImageIcon("images/carré.jpg"));
private RondAction rAction = new RondAction("Rond", new ImageIcon("images/rond.jpg"));
private TriangleAction tAction = new TriangleAction("Triangle", new ImageIcon("images/triangle.jpg"));
private EtoileAction eAction = new EtoileAction("Etoile", new ImageIcon("images/étoile.jpg"));
//Création des instances d'actions de couleurs de forme
private FormeRougeAction rfAction = new FormeRougeAction("Rouge");
private FormeBleuAction bfAction = new FormeBleuAction("Bleu");
private FormeVertAction vfAction = new FormeVertAction("Vert");
private FormeBlancAction wfAction = new FormeBlancAction("Blanc");
//Création des instances de couleurs de fond
private FondRougeAction rFondAction = new FondRougeAction("Rouge");
private FondBleuAction bFondAction = new FondBleuAction("Bleu");
private FondVertAction vFondAction = new FondVertAction("Vert");
private FondBlancAction wFondAction = new FondBlancAction("Blanc");
//Création des instances d'actions pour le lancement et l'arrêt
private LancerAction lAction = new LancerAction("Lancer l'animation", new ImageIcon("images/play.jpg"));
private ArretAction sAction = new ArretAction("Arrêter l'animation", new ImageIcon("images/stop.jpg"));
//***********************************************************
// La déclaration pour le menu de la JMenuBar
//***********************************************************
private JMenuBar menuBar = new JMenuBar();
private JMenu animation = new JMenu("Animation"),
forme = new JMenu("Forme"),
typeForme = new JMenu("Type de forme"),
aPropos = new JMenu("À propos");
private JMenuItem lancer = new JMenuItem(lAction),
arreter = new JMenuItem(sAction),
quitter = new JMenuItem("Quitter"),
aProposItem = new JMenuItem("?");
private JCheckBoxMenuItem morph = new JCheckBoxMenuItem("Morphing");
private JRadioButtonMenuItem carre = new JRadioButtonMenuItem(cAction),
rond = new JRadioButtonMenuItem(rAction),
triangle = new JRadioButtonMenuItem(tAction),
etoile = new JRadioButtonMenuItem(eAction);
private ButtonGroup bg = new ButtonGroup();
//***********************************************************
// La déclaration pour le menu contextuel
//***********************************************************
private JPopupMenu jpm = new JPopupMenu();
private JMenu background = new JMenu("Couleur de fond");
private JMenu couleur = new JMenu("Couleur de la forme");
private JMenuItem launch = new JMenuItem(lAction);
private JMenuItem stop = new JMenuItem(sAction);
private JMenuItem rouge = new JMenuItem(rfAction),
bleu = new JMenuItem(bfAction),
vert = new JMenuItem(vfAction),
blanc = new JMenuItem(wfAction),
rougeBack = new JMenuItem(rFondAction),
bleuBack = new JMenuItem(bFondAction),
vertBack = new JMenuItem(vFondAction),
blancBack = new JMenuItem(wFondAction);
//Création de notre barre d'outils
private JToolBar toolBar = new JToolBar();
//Les boutons
private JButton play = new JButton(lAction),
cancel = new JButton(sAction),
square = new JButton(cAction),
tri = new JButton(tAction),
circle = new JButton(rAction),
star = new JButton(eAction);
private Color fondBouton = Color.white;
public ZFenetre(){
this.setTitle("Animation");
this.setSize(800, 600);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
//On initialise le menu stop
stop.setEnabled(false);
//On crée et on passe l'écouteur pour afficher le menu contextuel
//Création d'une implémentation de MouseAdapter
//avec redéfinition de la méthode adéquate
pan.addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent event){
//Seulement s'il s'agit d'un clic droit
if(event.isPopupTrigger())
{
background.add(blancBack);
background.add(rougeBack);
background.add(bleuBack);
background.add(vertBack);
couleur.add(blanc);
couleur.add(rouge);
couleur.add(bleu);
couleur.add(vert);
jpm.add(launch);
jpm.add(stop);
jpm.add(couleur);
jpm.add(background);
//La méthode qui va afficher le menu
jpm.show(pan, event.getX(), event.getY());
}
}
});
container.add(pan, BorderLayout.CENTER);
this.setContentPane(container);
this.initMenu();
this.initToolBar();
this.setVisible(true);
}
private void initToolBar(){
this.cancel.setEnabled(false);
this.cancel.setBackground(fondBouton);
this.play.setBackground(fondBouton);
this.toolBar.setFont(new Font("Courier", Font.PLAIN, 0));
this.toolBar.add(play);
this.toolBar.add(cancel);
this.toolBar.addSeparator();
this.toolBar.add(rAction);
this.toolBar.add(cAction);
this.toolBar.add(tAction);
this.toolBar.add(eAction);
this.add(toolBar, BorderLayout.NORTH);
}
private void initMenu(){
//Menu animation
//****************************
//Ajout du listener pour lancer l'animation
//ATTENTION LE LISTENER EST GLOBAL ! ! ! !
//-----------------------------------------------
//On attribue l'accélerateur c
lancer.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L,
KeyEvent.CTRL_MASK));
animation.add(lancer);
//Ajout du listener pour arrêter l'animation
//LISTENER À CHANGER ICI AUSSI
//--------------------------------------------
arreter.setEnabled(false);
arreter.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
KeyEvent.CTRL_DOWN_MASK + KeyEvent.SHIFT_DOWN_MASK));
animation.add(arreter);
animation.addSeparator();
quitter.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
System.exit(0);
}
});
animation.add(quitter);
//Menu forme
bg.add(carre);
bg.add(triangle);
bg.add(rond);
bg.add(etoile);
//On crée un nouvel écouteur, inutile de créer 4 instances différentes
typeForme.add(rond);
typeForme.add(carre);
typeForme.add(triangle);
typeForme.add(etoile);
rond.setSelected(true);
forme.add(typeForme);
//Ajout du listener pour le morphing
morph.addActionListener(new MorphListener());
forme.add(morph);
//menu à propos
//Ajout de ce que doit faire le "?"
aProposItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
ImageIcon img = new ImageIcon("images/cysboy.gif");
String mess = "Merci ! \n J'espère que vous vous amusez bien ! \n";
mess += "Je crois qu'il est temps d'ajouter des accélérateurs et des mnémoniques dans tout ça...\n";
mess += "\n Allez, GO les ZérOs !";
jop.showMessageDialog(null, mess, "À propos", JOptionPane.INFORMATION_MESSAGE, img);
}
});
aPropos.add(aProposItem);
//Ajout des menus dans la barre de menus
animation.setMnemonic('A');
menuBar.add(animation);
forme.setMnemonic('F');
menuBar.add(forme);
aPropos.setMnemonic('P');
menuBar.add(aPropos);
//Ajout de la barre de menus sur la fenêtre
this.setJMenuBar(menuBar);
}
private void go(){
x = pan.getPosX();
y = pan.getPosY();
while(this.animated){
//System.out.println("OK");
//Si le mode morphing est activé, on utilise la taille actuelle de la forme
if(pan.isMorph())
{
if(x < 1)backX = false;
if(x > pan.getWidth() - pan.getDrawSize())backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight() - pan.getDrawSize())backY = true;
}
//Sinon, comme d'habitude
else
{
if(x < 1)backX = false;
if(x > pan.getWidth()-50)backX = true;
if(y < 1)backY = false;
if(y > pan.getHeight()-50)backY = true;
}
if(!backX)pan.setPosX(++x);
else pan.setPosX(--x);
if(!backY) pan.setPosY(++y);
else pan.setPosY(--y);
pan.repaint();
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Lance le thread.
* @author CHerby
*/
class PlayAnimation implements Runnable{
public void run() {
go();
}
}
/**
* Écoute le menu Morphing
* @author CHerby
*/
class MorphListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Si la case est cochée, activation du mode morphing
if(morph.isSelected())pan.setMorph(true);
//Sinon rien !
else pan.setMorph(false);
}
}
//*************************************************************
// LES CLASSES D'ACTIONS POUR LA FORME
//*************************************************************
/**
* Classe gérant le changement de la forme en carré
* @author CHerby
*/
class CarreAction extends AbstractAction{
/**
* Le constructeur prend le nom de l'icône en paramètre
* @param name
* @param img
*/
public CarreAction(String name, ImageIcon img){
super(name, img);
}
/**
* Celui-ci ne prend que le nom
* @param name
*/
public CarreAction(String name){
super(name);
}
/**
* L'action effectuée
*/
public void actionPerformed(ActionEvent e) {
pan.setForme("CARRE");
carre.setSelected(true);
}
}
//*************************************************************
class RondAction extends AbstractAction{
public RondAction(String name, ImageIcon img){
super(name, img);
}
public void actionPerformed(ActionEvent arg0) {
pan.setForme("ROND");
rond.setSelected(true);
}
}
//*************************************************************
class TriangleAction extends AbstractAction{
public TriangleAction(String name, ImageIcon img){
super(name, img);
}
public void actionPerformed(ActionEvent arg0) {
pan.setForme("TRIANGLE");
triangle.setSelected(true);
}
}
//*************************************************************
class EtoileAction extends AbstractAction{
public EtoileAction(String name, ImageIcon img){
super(name, img);
}
public void actionPerformed(ActionEvent arg0) {
pan.setForme("ETOILE");
etoile.setSelected(true);
}
}
//*************************************************************
// ACTIONS POUR LES COULEURS DE FOND
//*************************************************************
class FondRougeAction extends AbstractAction{
public FondRougeAction(String name){
super(name);
}
public void actionPerformed(ActionEvent arg0) {
pan.setCouleurFond(Color.red);
}
}
//*************************************************************
class FondBleuAction extends AbstractAction{
public FondBleuAction(String name){
super(name);
}
public void actionPerformed(ActionEvent arg0) {
pan.setCouleurFond(Color.blue);
}
}
//*************************************************************
class FondVertAction extends AbstractAction{
public FondVertAction(String name){
super(name);
}
public void actionPerformed(ActionEvent arg0) {
pan.setCouleurFond(Color.green);
}
}
//*************************************************************
class FondBlancAction extends AbstractAction{
public FondBlancAction(String name){
super(name);
}
public void actionPerformed(ActionEvent arg0) {
pan.setCouleurFond(Color.white);
}
}
//*************************************************************
// ACTIONS POUR LES COULEURS DE LA FORME
//*************************************************************
class FormeRougeAction extends AbstractAction{
public FormeRougeAction(String name){
super(name);
}
public void actionPerformed(ActionEvent arg0) {
pan.setCouleurForme(Color.red);
}
}
//*************************************************************
class FormeBleuAction extends AbstractAction{
public FormeBleuAction(String name){
super(name);
}
public void actionPerformed(ActionEvent arg0) {
pan.setCouleurForme(Color.blue);
}
}
//*************************************************************
class FormeVertAction extends AbstractAction{
public FormeVertAction(String name){
super(name);
}
public void actionPerformed(ActionEvent arg0) {
pan.setCouleurForme(Color.green);
}
}
//*************************************************************
class FormeBlancAction extends AbstractAction{
public FormeBlancAction(String name){
super(name);
}
public void actionPerformed(ActionEvent arg0) {
pan.setCouleurForme(Color.white);
}
}
//*************************************************************
// ACTIONS POUR LE LANCEMENT/ARRÊT
//*************************************************************
class ArretAction extends AbstractAction{
public ArretAction(String name, ImageIcon img){
super(name, img);
}
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous arrêter l'animation ?", "Arrêt de l'animation", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if(option != JOptionPane.NO_OPTION && option != JOptionPane.CANCEL_OPTION && option != JOptionPane.CLOSED_OPTION)
{
animated = false;
//On remplace nos boutons par nos MenuItem
lancer.setEnabled(true);
arreter.setEnabled(false);
//ON AJOUTE L'INSTRUCTION POUR LE MENU CONTEXTUEL
//************************************************
launch.setEnabled(true);
stop.setEnabled(false);
play.setEnabled(true);
cancel.setEnabled(false);
}
}
}
//*************************************************************
class LancerAction extends AbstractAction{
public LancerAction(String name, ImageIcon img){
super(name, img);
}
public void actionPerformed(ActionEvent arg0) {
JOptionPane jop = new JOptionPane();
int option = jop.showConfirmDialog(null, "Voulez-vous lancer l'animation ?", "Lancement de l'animation", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE);
if(option == JOptionPane.OK_OPTION)
{
lancer.setEnabled(false);
arreter.setEnabled(true);
//ON AJOUTE L'INSTRUCTION POUR LE MENU CONTEXTUEL
//************************************************
launch.setEnabled(false);
stop.setEnabled(true);
play.setEnabled(false);
cancel.setEnabled(true);
animated = true;
t = new Thread(new PlayAnimation());
t.start();
}
}
}
}
|
Vous pouvez voir que le code
s'est un peu alourdi...
Mais, comme je vous le disais, c'est une question de
choix et de conception.
Bon, je crois qu'un topo et un QCM vous
attendent...
Ce qu'il faut retenir
- L'objet pour mettre une barre de menu sur vos IHM swing est un
JMenuBar.
- Dans cet objet, vous pouvez mettre des objets JMenu afin de créer
un menu déroulant.
- L'objet cité ci-dessus accepte des objets JMenu, JMenuItem, JCheckBoxMenuItem et
JRadioButtonMenuItem.
- Afin d'interagir avec vos points de menu, vous pouvez utiliser une
implémentation de l'interface ActionListener.
- Pour faciliter l'accès aux menus de la barre de menus, vous pouvez ajouter
des mnémoniques à ceux-ci.
- L'ajout d'accélérateurs permet
de déclencher des actions, le plus souvent, par des combinaisons de touches.
- Afin de pouvoir récupérer les codes des touches du clavier, vous devrez
utiliser un objet KeyStroke ainsi qu'un objet
KeyEvent.
- Les menus contextuels fonctionnent tout comme un menu normal, à la
différence qu'il s'agit d'un objet JPopupMenu.
- Vous devez toutefois spécifier sur quel composant doit s'afficher le menu
contextuel.
- La détection du clic droit se fait grâce à la méthode isPopupTrigger() de l'objet MouseEvent.
- L'ajout d'une barre d'outils nécessite l'utilisation de l'objet
JToolBar.
Vous avez encore appris beaucoup de choses dans ce
chapitre...
Je vous rassure, vous n'êtes pas au bout de vos peines !

Il
nous reste encore tant de choses à voir. D'ailleurs, vous vous demandez
peut-être comment faire pour créer un menu "Enregistrer" ou encore "Enregistrer
sous" ?
Avant de voir comment faire ceci, nous allons passer par un petit
TP :
L'ardoise mazique !
TP : l'ardoise maZique
Nous voilà partis pour un nouveau TP.
Dans celui-ci,
ce qui prime c'est surtout :
- d'utiliser les menus, les accélérateurs et mnémoniques ;
- d'ajouter une barre d'outils ;
- de créer des implémentations et savoir les utiliser sur plusieurs
composants ;
- d'utiliser des classes anonymes ;
- ...
Je ne vous demande pas de faire un logiciel
hyper-pointu !

Vous
constaterez d'ailleurs qu'il peut y avoir des bugs d'affichage... Je ne vous dis
pas lesquels, on ne sait jamais, si vous ne les trouvez pas...
Ce n'est
pas tout ça mais... si on commençait ?
Cahier des charges
Voilà les recommandations.
Vous devez faire une
sorte d'ardoise magique. En gros, celle-ci devra être composée d'un
JPanel
amélioré (ça sent l'héritage...) sur lequel vous pourrez tracer des choses en
cliquant et en déplaçant la souris.
Vos tracés devront être effectués
point par point, ceux-ci d'une taille que je vous laisse apprécier. Par contre,
vous devrez pouvoir utiliser deux sortes de "
pinceaux" :
Vous aurez aussi la possibilité de changer la
couleur de vos traits, les couleurs que j'ai choisies sont :
- le bleu ;
- le rouge ;
- le vert.
Il faut
OBLIGATOIREMENT :
- un menu avec accélérateurs et mnémoniques ;
- une barre d'outils avec les formes et les couleurs ;
- un menu "Quitter" et un menu
"Effacer" ;
- que les formes et les couleurs soient accessibles via le menu !
Voici ce que j'ai obtenu :
Et
voilà ce que j'ai fait rien que pour vous :
Attention :
vous allez utiliser la méthode repaint() de
votre composant ; cependant, souvenez-vous que celle-ci est appelée
automatiquement lors du redimensionnement de votre fenêtre, de
la réduction, de l'agrandissement... Vous allez devoir gérer ce cas de figure !
Sinon, votre zone de dessin s'effacera à chaque redimensionnement !
Je vous conseille de créer une classe
Point qui va contenir
les informations relatives à un point tracé (couleur, taille,
position...).
Il va falloir que vous gériez une collection de points
(générique, la collection

) dans votre classe dérivée de
JPanel !
J'en ai presque trop
dit...
Bon, je suis un ange aujourd'hui.

Voici
les images que j'ai utilisées :
Je
sais... Elles ne sont pas terribles.

Mais
le but n'est pas là !
Avant de vous lancer dans votre code, vous devez
savoir quelques choses...
Prérequis
Afin que vous puissiez faire des tracés, vous allez
devoir détecter le mouvement de votre souris.
Je ne vous ai pas encore parlé
de ça auparavant, mais vous avez l'habitude d'utiliser des interfaces de gestion
d'événements, maintenant...
Afin de détecter les mouvements de votre
souris, vous allez devoir utiliser l'interface
MouseMotionListener ; celle-ci contient
deux méthodes :
- mouseMoved(MouseEvent
e) : détecte le mouvement de la souris sur le composant
;
- mouseDragged(MouseEvent
e) : idem que ci-dessus mais vous devrez avoir cliqué
sur le composant et maintenir ce clic enfoncé pendant le mouvement (exactement
ce qu'il vous faut
).
Voilà : vous allez devoir créer une implémentation de cette
interface pour réussir à dessiner sur votre conteneur !
Bon ! Ne vous
hâtez pas trop, réfléchissez bien à ce dont vous avez besoin. Comment utiliser
vos implémentations... Bref, surtout ne pas vous précipiter !
Un code bien
réfléchi est un code rapidement opérationnel !

C'est
à vous, maintenant...
À vos claviers.
Correction
Roulement de tambour... C'est le moment de vérité !
J'ai mis des images sur mes boutons de barre
d'outils...
Secret (cliquez
pour afficher)
Point.java
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 |
import java.awt.Color;
public class Point {
//Couleur du point
private Color color = Color.red;
//Taille
private int size = 10;
//position sur l'axe X : initialisé au dehors du conteneur
private int x = -10;
//Position sur l'axe Y : initialisé au dehors du conteneur
private int y = -10;
//Type de point
private String type = "ROND";
/**
* Constructeur par défaut
*/
public Point(){}
/**
* Constructeur avec paramètres
* @param x
* @param y
* @param size
* @param color
* @param type
*/
public Point(int x, int y, int size, Color color, String type){
this.size = size;
this.x = x;
this.y = y;
this.color = color;
this.type = type;
}
//****************************************
// ACCESSEURS
//****************************************
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
|
DrawPanel.java
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 |
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import javax.swing.JPanel;
public class DrawPanel extends JPanel{
//Couleur du pointeur
private Color pointerColor = Color.red;
//Forme du pointeur
private String pointerType = "CIRCLE";
//Position X du pointeur
private int posX = -10, oldX = -10;
//Position Y du pointeur
private int posY = -10, oldY = -10;
//pour savoir si on doit dessiner ou non
private boolean erasing = true;
//Taille du pointeur
private int pointerSize = 15;
//Collection de points !
private ArrayList<Point> points = new ArrayList<Point>();
/**
* Constructeur
*/
public DrawPanel(){
this.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
points.add(new Point(e.getX() - (pointerSize / 2), e.getY() - (pointerSize / 2), pointerSize, pointerColor, pointerType));
repaint();
}
});
this.addMouseMotionListener(new MouseMotionListener(){
public void mouseDragged(MouseEvent e) {
//On récupère les coordonnées de la souris
//et on enlève la moitié de la taille du pointeur
//pour centrer le tracé
points.add(new Point(e.getX() - (pointerSize / 2), e.getY() - (pointerSize / 2), pointerSize, pointerColor, pointerType));
repaint();
}
public void mouseMoved(MouseEvent e) {}
});
}
/**
* Vous la connaissez maintenant, celle-là ;)
*/
public void paintComponent(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
//Si on doit effacer, on ne passe pas dans le else => pas de dessin
if(this.erasing){
this.erasing = false;
}
else{
//On parcourt notre collection de points
for(Point p : this.points)
{
//On récupère la couleur
g.setColor(p.getColor());
//Selon le type de point
if(p.getType().equals("SQUARE")){
g.fillRect(p.getX(), p.getY(), p.getSize(), p.getSize());
}
else{
g.fillOval(p.getX(), p.getY(), p.getSize(), p.getSize());
}
}
}
}
/**
* Efface le contenu
*/
public void erase(){
this.erasing = true;
this.points = new ArrayList<Point>();
repaint();
}
/**
* Définit la couleur du pointeur
* @param c
*/
public void setPointerColor(Color c){
this.pointerColor = c;
}
/**
* Définit la forme du pointeur
* @param str
*/
public void setPointerType(String str){
this.pointerType = str;
}
}
|
Fenetre.java
Code : Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188 |
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
public class Fenetre extends JFrame {
//**************************************
// LE MENU
//**************************************
private JMenuBar menuBar = new JMenuBar();
JMenu fichier = new JMenu("Fichier"),
edition = new JMenu("Edition"),
forme = new JMenu("Forme du pointeur"),
couleur = new JMenu("Couleur du pointeur");
JMenuItem nouveau = new JMenuItem("Effacer"),
quitter = new JMenuItem("Quitter"),
rond = new JMenuItem("Rond"),
carre = new JMenuItem("Carré"),
bleu = new JMenuItem("Bleu"),
rouge = new JMenuItem("Rouge"),
vert = new JMenuItem("Vert");
//****************************************
// LA BARRE D'OUTILS
//****************************************
JToolBar toolBar = new JToolBar();
JButton square = new JButton(new ImageIcon("images/carré.jpg")),
circle = new JButton(new ImageIcon("images/rond.jpg")),
red = new JButton(new ImageIcon("images/rouge.jpg")),
green = new JButton(new ImageIcon("images/vert.jpg")),
blue = new JButton(new ImageIcon("images/bleu.jpg"));
//***************************************
// LES ÉCOUTEURS
//***************************************
private FormeListener fListener = new FormeListener();
private CouleurListener cListener = new CouleurListener();
//Notre zone de dessin
private DrawPanel drawPanel = new DrawPanel();
public Fenetre(){
this.setSize(700, 500);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//On initialise le menu
this.initMenu();
//Idem pour la barre d'outils
this.initToolBar();
//On positionne notre zone de dessin
this.getContentPane().add(drawPanel, BorderLayout.CENTER);
this.setVisible(true);
}
/**
* Initialise le menu
*/
private void initMenu(){
nouveau.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
drawPanel.erase();
}
});
nouveau.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK));
quitter.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
});
quitter.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK));
fichier.add(nouveau);
fichier.addSeparator();
fichier.add(quitter);
fichier.setMnemonic('F');
carre.addActionListener(fListener);
rond.addActionListener(fListener);
forme.add(rond);
forme.add(carre);
rouge.addActionListener(cListener);
vert.addActionListener(cListener);
bleu.addActionListener(cListener);
couleur.add(rouge);
couleur.add(vert);
couleur.add(bleu);
edition.setMnemonic('E');
edition.add(forme);
edition.addSeparator();
edition.add(couleur);
menuBar.add(fichier);
menuBar.add(edition);
this.setJMenuBar(menuBar);
}
/**
* Initialise l barre d'outils
*/
private void initToolBar(){
JPanel panneau = new JPanel();
square.addActionListener(fListener);
circle.addActionListener(fListener);
red.addActionListener(cListener);
green.addActionListener(cListener);
blue.addActionListener(cListener);
toolBar.add(square);
toolBar.add(circle);
toolBar.addSeparator();
toolBar.add(red);
toolBar.add(blue);
toolBar.add(green);
this.getContentPane().add(toolBar, BorderLayout.NORTH);
}
//ÉCOUTEUR POUR LE CHANGEMENT DE FORME
//************************************
class FormeListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
if(e.getSource().getClass().getName().equals("javax.swing.JMenuItem")){
if(e.getSource()==carre)drawPanel.setPointerType("SQUARE");
else drawPanel.setPointerType("CIRCLE");
}
else{
if(e.getSource()==square)drawPanel.setPointerType("SQUARE");
else drawPanel.setPointerType("CIRCLE");
}
}
}
//ÉCOUTEUR POUR LE CHANGEMENT DE COULEUR
//**************************************
class CouleurListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println(e.getSource().getClass().getName());
if(e.getSource().getClass().getName().equals("javax.swing.JMenuItem")){
System.out.println("OK !");
if(e.getSource()==vert)drawPanel.setPointerColor(Color.green);
else if(e.getSource()==bleu)drawPanel.setPointerColor(Color.blue);
else drawPanel.setPointerColor(Color.red);
}
else{
if(e.getSource()==green)drawPanel.setPointerColor(Color.green);
else if(e.getSource()==blue)drawPanel.setPointerColor(Color.blue);
else drawPanel.setPointerColor(Color.red);
}
}
}
public static void main(String[] args){
Fenetre fen = new Fenetre();
}
}
|
Vous avez pu
voir que c'est un code assez simple. Il n'y a rien de difficile et surtout, il a
le mérite de vous faire travailler un peu tout ce que vous avez vu
jusqu'ici...
Comme je vous l'ai dit dans l'introduction, ce code n'est
pas parfait et il n'avait pas vocation à l'être... Je sais que vous êtes assez
calés en Java pour avoir ajouté plus de fonctionnalités que moi sur ce TP. Mon
code est une solution, ET NON PAS LA SOLUTION.
Améliorations possibles
Voici ce que vous pouvez faire afin de rendre cette
application plus attractive :
- permettre de changer la taille du pinceau ;
- avoir une plus grande palette de couleurs ;
- avoir des pinceaux supplémentaires ;
- créer une gomme ;
- ...
Vous voyez que ce ne sont pas les idées qui
manquent...
Vous pouvez essayer de faire ce TP avec ces suppléments. Prenez
ça comme une version plus difficile du TP, et encore...
Bon. Nous sommes
à la fin de ce TP. Retour au tuto.
Même s'il n'est pas parfait, ce TP était tout de même
marrant à faire...

Bon,
la récréation est finie.
Je sais que certains Zér0s ont une envie folle de
créer des menus comme "
Enregistrer" ou
"
Enregistrer sous...".
Mais,
avant d'en arriver là, nous allons devoir aborder
Les
flux d'entrée / de sortie !
Cette partie est encore en cours d'édition...
Je m'y remets dès que
possible ! Promis...